传智播客风清扬视频-------线程简介
想了解线程,必须先了解进程,因为线程是依赖进程存在的。
什么是进程?
进程就是正在运行的程序;是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
多进程有什么意义?
单进程的计算机只能做一件事情,而我们现在的计算机可以做多件事情。
举例:一边玩游戏,一边听音乐等
现在的计算机都支持多进程的,可以在一个时间段内执行多个任务。并且,可以提高CPU使用率。
什么是线程?
在同一个进程内又可以执行多个任务,而每一个任务可以看成是一个线程。
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。
单线程:如果程序只有一条执行路径。
多线程:如果程序有多条执行路径。
多线程有什么意义呢?
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的机率抢到CPU的执行权。
我们不敢保证哪一个线程能够在哪个时刻抢到,所以线程执行有随机性。
并行和并发的区别:
并行:是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发:是物理上同时发生,指在某一个时间点同时运行多个程序。
Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动一个进程。
接着有该进程创建了一个主线程去调main方法。
方法1:继承Thread实现线程
步骤:
A:自定义类MyThread继承Thread类
B:MyThread类里面重写run()
C:创建对象
D:启动线程
该类为什么重写run()方法? 不是类中所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,Java提供了run方法来包含被线程执行的代码。
public class MyThread extends Thread {
@Override public void run() { // 一般来说,被线程执行的代码肯定是比较耗时的。 // 所以我们用循环改进 for (int x = 0; x < 200; x++) { System.out.println(x); } }
}
public class MyThreadDemo { public static void main(String[] args) { // 创建线程对象 // MyThread my = new MyThread(); // // 启动线程 // my.run(); // my.run(); // 调用run()方法为什么是单线程的呢? // 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果 // 要想看到多线程的效果,就必须说说另一个方法:start() // 面试题:run()和start()的区别? // run():仅仅是封装被线程执行的代码,直接调用是普通方法 // start():首先启动了线程,然后再由jvm去调用该线程的run()方法。 // MyThread my = new MyThread(); // my.start(); // // IllegalThreadStateException:非法的线程状态异常 // // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。 // my.start();
// 创建两个线程对象 MyThread my1 = new MyThread(); MyThread my2 = new MyThread();
my1.start(); my2.start(); }}
获取线程对象的名称:
public final String getName() :获取线程对象的名称
设置线程对象的名称:
public final void setName()
针对不是Thread类的子类中如何获取线程对象名称
public static Thread currentThread() : 返回当前正在执行的线程对象
Thread.currentThread().getName()
public class MyThreadDemo { public static void main(String[] args) { // 创建线程对象 //无参构造+setXxx() // MyThread my1 = new MyThread(); // MyThread my2 = new MyThread(); // //调用方法设置名称 // my1.setName("林青霞"); // my2.setName("刘意"); // my1.start(); // my2.start();
//带参构造方法给线程起名字 // MyThread my1 = new MyThread("林青霞"); // MyThread my2 = new MyThread("刘意"); // my1.start(); // my2.start();
//我要获取main方法所在的线程对象的名称,该怎么办呢? //遇到这种情况,Thread类提供了一个很好玩的方法: //public static Thread currentThread():返回当前正在执行的线程对象 System.out.println(Thread.currentThread().getName()); }}
public final void setDaemon(boolean on)将线程设置为守护线程或用户线程
当正在运行的线程都是守护线程时,Java虚拟机退出, 该方法必须在启动前调用
public class ThreadDaemon extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } }}
public class ThreadDaemonDemo { public static void main(String[] args) { ThreadDaemon td1 = new ThreadDaemon(); ThreadDaemon td2 = new ThreadDaemon();
td1.setName("关羽"); td2.setName("张飞");
// 设置守护线程 td1.setDaemon(true); td2.setDaemon(true);
td1.start(); td2.start();
Thread.currentThread().setName("刘备"); for (int x = 0; x < 5; x++) { System.out.println(Thread.currentThread().getName() + ":" + x); } }}
public final void join() ; 等待该线程终止后再调用其它线程
public class ThreadJoinDemo { public static void main(String[] args) { ThreadJoin tj1 = new ThreadJoin(); ThreadJoin tj2 = new ThreadJoin(); ThreadJoin tj3 = new ThreadJoin();
tj1.setName("李渊"); tj2.setName("李世民"); tj3.setName("李元霸");
tj1.start(); try { tj1.join(); } catch (InterruptedException e) { e.printStackTrace(); }
tj2.start(); tj3.start(); }}
设置线程对象优先级
public final int getPriority() :返回线程对象优先级
public final void setPriority(int ne) : 设置线程优先级
注意:
线程默认优先级是5
线程优先级的范围是 : 1-10
线程优先级高仅表示线程获取的CPU时间片的几率高,需要在次数比较多,或多次运行时效果才比较显著。
IllegalArgumentException:非法参数异常。
抛出的异常表明向方法传递了一个不合法或不正确的参数。
public class ThreadPriority extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); } }}
public class ThreadPriorityDemo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority();
tp1.setName("东方不败"); tp2.setName("岳不群"); tp3.setName("林平之");
// 获取默认优先级 // System.out.println(tp1.getPriority()); // System.out.println(tp2.getPriority()); // System.out.println(tp3.getPriority());
// 设置线程优先级 // tp1.setPriority(100000);
//设置正确的线程优先级 tp1.setPriority(10); tp2.setPriority(1);
tp1.start(); tp2.start(); tp3.start(); }}
public static void sleep() 线程休眠
public class ThreadSleep extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x + ",日期:" + new Date()); // 睡眠 // 困了,我稍微休息1秒钟 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
public class ThreadSleepDemo { public static void main(String[] args) { ThreadSleep ts1 = new ThreadSleep(); ThreadSleep ts2 = new ThreadSleep(); ThreadSleep ts3 = new ThreadSleep();
ts1.setName("林青霞"); ts2.setName("林志玲"); ts3.setName("林志颖");
ts1.start(); ts2.start(); ts3.start(); }}
线程中断
public final void stop() : 让线程停止,过时了, 超过一定时间,直接结束进程
public final void interrupt() : 中断线程,把线程的状态终止,并抛出一个InterruptedException。
public class ThreadStop extends Thread { @Override public void run() { System.out.println("开始执行:" + new Date());
// 我要休息10秒钟,亲,不要打扰我哦 try { Thread.sleep(10000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("线程被终止了"); }
System.out.println("结束执行:" + new Date()); }}
public class ThreadStopDemo { public static void main(String[] args) { ThreadStop ts = new ThreadStop(); ts.start();
// 你超过三秒不醒过来, try { Thread.sleep(3000); // ts.stop(); // 结束线程 ts.interrupt(); //抛出异常,将线程状态终止 } catch (InterruptedException e) { e.printStackTrace(); } }}
public static void yield() : 暂停当前正在执行的线程,让CPU去执行其它线程
让多个线程执行更和谐
public class ThreadYield extends Thread { @Override public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + ":" + x); Thread.yield(); } }}
public class ThreadYieldDemo { public static void main(String[] args) { ThreadYield ty1 = new ThreadYield(); ThreadYield ty2 = new ThreadYield();
ty1.setName("林青霞"); ty2.setName("刘意");
ty1.start(); ty2.start(); }}
方法二: 实现Runnable接口
步骤 : A:自定义MyRunnable实现Runnable接口
B:重写run()方法
C:创建MyRunnable类的对象
D:创建Thread类的对象,并把c步骤的对象作为构造参数传递
public class MyRunnable implements Runnable {
@Override public void run() { for (int x = 0; x < 100; x++) { // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用 System.out.println(Thread.currentThread().getName() + ":" + x); } }
}
public class MyRunnableDemo { public static void main(String[] args) { // 创建MyRunnable类的对象 MyRunnable my = new MyRunnable();
// 创建Thread类的对象,并把C步骤的对象作为构造参数传递 // Thread(Runnable target) // Thread t1 = new Thread(my); // Thread t2 = new Thread(my); // t1.setName("林青霞"); // t2.setName("刘意");
// Thread(Runnable target, String name) Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "刘意");
t1.start(); t2.start(); }}
案例:模拟售票
版本1 : 继承Thread类
public class SellTicket extends Thread {
// 定义100张票 // private int tickets = 100; // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰 private static int tickets = 100;
@Override public void run() { // 定义100张票 // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面 // int tickets = 100;
// 是为了模拟一直有票 while (true) { if (tickets > 0) { System.out.println(getName() + "正在出售第" + (tickets--) + "张票"); } } }}
public class SellTicketDemo { public static void main(String[] args) { // 创建三个线程对象 SellTicket st1 = new SellTicket(); SellTicket st2 = new SellTicket(); SellTicket st3 = new SellTicket();
// 给线程对象起名字 st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3");
// 启动线程 st1.start(); st2.start(); st3.start(); }}
版本二: 实现Runnable接口
public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100;
@Override public void run() { while (true) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); } } }}
public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket();
// 创建三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3");
// 启动线程 t1.start(); t2.start(); t3.start(); }}
版本三:通过sleep() 暴露出问题
public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100;
// @Override// public void run() {// while (true) {// // t1,t2,t3三个线程// // 这一次的tickets = 100;// if (tickets > 0) {// // 为了模拟更真实的场景,我们稍作休息// try {// Thread.sleep(100); // t1就稍作休息,t2就稍作休息// } catch (InterruptedException e) {// e.printStackTrace();// }//// System.out.println(Thread.currentThread().getName() + "正在出售第"// + (tickets--) + "张票");// // 理想状态:// // 窗口1正在出售第100张票// // 窗口2正在出售第99张票// // 但是呢?// // CPU的每一次执行必须是一个原子性(最简单基本的)的操作。// // 先记录以前的值// // 接着把ticket--// // 然后输出以前的值(t2来了)// // ticket的值就变成了99// // 窗口1正在出售第100张票// // 窗口2正在出售第100张票//// }// }// }
@Override public void run() { while (true) { // t1,t2,t3三个线程 // 这一次的tickets = 1; if (tickets > 0) { // 为了模拟更真实的场景,我们稍作休息 try { Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息, } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); //窗口1正在出售第1张票,tickets=0 //窗口2正在出售第0张票,tickets=-1 //窗口3正在出售第-1张票,tickets=-2 } } }}
/* * 实现Runnable接口的方式实现 * * 通过加入延迟后,就产生了连个问题: * A:相同的票卖了多次 * CPU的一次操作必须是原子性的 * B:出现了负数票 * 随机性和延迟导致的 */public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket();
// 创建三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3");
// 启动线程 t1.start(); t2.start(); t3.start(); }}
如何解决线程安全问题?
分析哪些原因导致问题:
A: 是否是多线程环境
B: 是否共享数据
C: 是否有多条语句操作共享数据
解决思路:
把多条语句操作共享数据的代码给包成一个整体,让某一个线程在执行的时候,别的线程不能进入。
同步代码块:
synchronized(对象){
需要同步的代码
}
注意: 同步可以解决安全问题的根本原因就在那个对象上,该对象如同锁的功能。
多个线程必须是同一把锁。
版本四:使用synchronized 同步解决问题
public class SellTicket implements Runnable { // 定义100张票 private int tickets = 100; //创建锁对象 private Object obj = new Object();
// @Override// public void run() {// while (true) {// synchronized(new Object()){// if (tickets > 0) {// try {// Thread.sleep(100); // } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "正在出售第"// + (tickets--) + "张票");// }// }// }// }
@Override public void run() { while (true) { synchronized (obj) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票"); } } } }}
public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket();
// 创建三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3");
// 启动线程 t1.start(); t2.start(); t3.start(); }}
同步的好处: 可以解决多线程的安全问题
同步的弊端: 当线程相当多时,因为每个线程都会去判断同步上的锁,
这是很耗资源的,无形中降低了程序运行的效率。
A:同步代码块的锁对象是: 任意对象
B:同步方法的格式及锁对象:
把同步方法关键字加在方法上
同步方法的锁对象是this
C:静态方法及代码块是: 类的字节码对象
public class SellTicket implements Runnable {
// 定义100张票 private static int tickets = 100;
// 定义同一把锁 private Object obj = new Object(); private Demo d = new Demo();
private int x = 0;
//同步代码块用obj做锁// @Override// public void run() {// while (true) {// synchronized (obj) {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票 ");// }// }// }// }
//同步代码块用任意对象做锁// @Override// public void run() {// while (true) {// synchronized (d) {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票 ");// }// }// }// }
@Override public void run() { while (true) { if(x%2==0){ synchronized (SellTicket.class) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); } } }else {// synchronized (d) {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票 ");// }// }
sellTicket();
} x++; } }
// private void sellTicket() {// synchronized (d) {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票 ");// }// }// }
//如果一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢?// private synchronized void sellTicket() {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName()// + "正在出售第" + (tickets--) + "张票 ");// }// }
private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); }}}
class Demo {}
public class SellTicketDemo { public static void main(String[] args) { // 创建资源对象 SellTicket st = new SellTicket();
// 创建三个线程对象 Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3");
// 启动线程 t1.start(); t2.start(); t3.start(); }}
将集合变成线程安全
public class ThreadDemo { public static void main(String[] args) { // 线程安全的类 StringBuffer sb = new StringBuffer(); Vector<String> v = new Vector<String>(); Hashtable<String, String> h = new Hashtable<String, String>();
// Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你 // 那么到底用谁呢? // public static <T> List<T> synchronizedList(List<T> list) List<String> list1 = new ArrayList<String>();// 线程不安全 List<String> list2 = Collections .synchronizedList(new ArrayList<String>()); // 线程安全 }}
总结:
(1)多线程: 一个应用程序多条执行路径
进程: 正在执行的应用程序
线程: 进程的执行单元,执行路径
单线程: 一个应用程序只有一条执行路径
多线程: 一个应用程序有多条执行路径
多进程的意义: 提高CPU的使用率
多线程的意义: 提高应用程序的使用率
(2)Java程序的运行原理及JVM的启动是多线程的吗?
A:JAVA命令去启动JVM,JVM就会启动一个进程,该进程会启动主线程。
B:JVM的启动是多线程的,因为最低有两个线程启动主线程和垃圾回收线程
(3)多线程实现方案
A:继承Thread类
B:实现Runnable接口
(4)线程的调度和优先级问题
A:线程的调度 a:分时调度 b:抢占式调度(java采用该种)
B:获取和设置线程优先级 a默认是5 b范围是1-10
(5)线程的控制
A:休眠线程 B:加入线程 C:礼让线程 D:后台线程 E:终止线程
(6)线程的生命周期
A:新建 B:就绪 C:运行 D:阻塞 E:死亡
(7)多线程安全问题的原因
A:是否有多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据
(8)同步解决线程安全问题
A:同步代码块
synchronized(对象){
需要被同步的代码
}
这里的锁对象可以是任意对象
B:同步方法
把同步加在方法上
这里的锁对象是this
C:静态同步方法
把同步加在方法上
这里的锁对象是当前类的字节码文件对象
(9)回顾以前的线程安全的类
A:StringBuffer
B:Vector
C:Hashtable
D:如何把一个线程不安全的集合类变成一个线程安全的集合类
用Collections工具类的方法即可。
传智播客风清扬视频-------线程简介相关推荐
- 传智播客风清扬视频-------网络编程简介
计算机网络模型 OSI(Open System Interconnection开放系统互连)参考模型 TCP/IP参考模型 OSI 应用层--表示层--会话层--传输层--网络层--数据链路层--物理 ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 卷 ba ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)
卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │ 1.txt │ c语言经典案例效果图示.doc │ ├─1传智播客_尹成_C语言从菜鸟到 ...
- 传智播客C语言视频第二季(第一季基础上增加诸多C语言案例讲解,有效下载期为10.5-10.10关闭)...
卷 backup 的文件夹 PATH 列表 卷序列号为 00000025 D4A8:14B0 J:. │ 1.txt │ c语言经典案例效果图示.doc │ ├─1传智播客_尹成_C语言从菜鸟到 ...
- 传智播客C语言视频第二季 第一季基础上增加诸多C语言案例讲解,有效下载期为10 5-10 10关闭
分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 卷 backup ...
- 传智播客-php基础视频,传智播客PHP核心基础视频教程推荐(资源)
PHP,是英文超级文本预处理语言Hypertext Preprocessor的缩写.PHP 是一种 HTML 内嵌式的语言,是一种在服务器端执行的嵌入HTML文档的脚本语言,语言的风格有类似于C语言, ...
- java工作流 传智播客_Activiti工作流视频教学(企业开发实战讲解)_传智播客
Activiti工作流视频教学(企业开发实战讲解)_传智播客课程简介: Activiti工作流视频教学(企业开发实战讲解)_传智播客本教学共分4天进行讲解,本站提供第1天内容在线观看,全集教学请在本站 ...
- 传智播客软件测试学习视频汇总:
课程名称 分类 URL 提取码 软件测试入门到精通 视频 http://yun.itheima.com/course/490.html?aoe cnj1 资料 https://pan.baidu.co ...
- Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)
1.守护线程 代码如下: import java.util.concurrent.locks.*; public class StopTest implements Runnable {private ...
最新文章
- 工作流编程循序渐进(9:使用本地服务在宿主和工作流之间通信)
- ab plc软件_回收三菱PLC模块西门子模块AB模块数控模块单片机回收【三菱plc吧】...
- Scrapy框架的学习(3.pipeline介绍以及多个爬虫的pipeline的使用)
- sklearn机器学习常用过程总结
- 关于SAP Spartacus Routing 页面上下文切换机制的实现
- leetcode860. 柠檬水找零
- Python-装饰器进阶
- Unity读取TXT文本文件
- 天秀!花费 200W 设计的新版 “小米”图标,看看用Python怎么绘制?
- C语言 Linux网络编程(C/S架构) 在线词典
- 测试理论----软件测试四大测试过程
- 卡塔编程_量子卡塔教您如何在Q#中进行量子编程
- 小攻是鸿蒙小受是鲲鹏,洪荒之鸿蒙大天尊
- 论文被引上千次,GitHub 开源6000星,他们是首届字节跳动奖学金获奖者
- 架构设计:网络附属存储NAS,块存储EBS与对象存储OSS的比较以及选用
- 知道创宇发布统一云防御,“开明兽”亮相“山海·创”
- 保定计算机软件学院是哪个区,河北软件职业技术学院在哪个区
- 高清壁纸免费下载网站
- 细粒度图像分析综述2019
- 快速入门GORM,使用GORM进行CURD
热门文章
- 全国城市根据首字母分类json格式
- hive币涨幅空间大吗_HIVE币今日价格_HIVE币最新消息_HIVE币行情走势图 - 币界网
- CSS基础-04-浏览器调试
- Camera和Image sensor技术基础笔记(4) -- 白平衡White Balance
- 南开1809计算机应用基础在线作业,【奥鹏】南开21春学期(1709、1803、1809、1903、1909、2003、2009、2103)《计算机应用基础》在线作业1...
- 二、PyQtGragh模块安装以及上手体验
- 百数在线表单如何实现表单套打?
- C语言编程计算下列算式的值
- python中的堆栈
- Mysql中数据类型括号中的数字代表的含义