基本概念
- 程序(program)=数据结构 + 算法,一段静态的代码
- 进程(process):程序的一次执行过程。
程序是静态的,进程是动态的
进程是资源分配的基本单位,系统在运行时会为每个进程分配不同的内存区域。
- 线程(Thread):程序内部的一条执行路径
若一个进程同一时间并行执行多个线程,就是支持多线程的
线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
若一个进程中的多个线程共享相同的内存单元/内存地址空间->虽然使得线程之间的通信更简便,高效。但多个线程操作共享的系统资源可能会带来安全隐患。
Java中,方法区和堆一个进程一份,虚拟栈和程序计数器一个线程一份。
单核CPU:一种假的多线程,并发
多核CPU:并行
多线程的优势
- 提高应用程序的响应。对图形化界面更有意义,可以增强用户体验。
- 提高CPU的利用率
- 改善程序结构。将既长又复杂的进程分成多个线程,独立运行,利于理解和修改
什么时候用多线程
- 程序需要同时执行两个或多个线程
- 程序需要实现一些需要等待的任务时,例如,用户输入,文件读写操作,网络操作,搜索等。
- 需要一些后台运行的程序
线程的创建和使用
方式1:继承Thread类
package vip.zdkk.java;/*** @author: zdkk* @create 2022-05-11 17:18** 多线程的创建方式1:* 1. 继承Thread类* 2. 重写Thread类run方法 -> 写需要执行的操作* 3. 创建Thread类子类的对象* 4. 通过此对象调用start()方法 -> ①启动当前线程 ②调用当前线程的run()* 不能用该对象再调用start()方法,会抛出 IllegalThreadStateException 异常,需要重新创建一个对象** 例:遍历100以内所有的偶数*/class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {if ((i % 2) == 0) {System.out.println(Thread.currentThread().getName() + " " + i);}}}}public class ThreadTest {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();for (int i = 0; i < 100; i++) {if ((i % 2) == 1) {System.out.println(Thread.currentThread().getName() + " " + i);}}new MyThread().start();}}
// 输出main 1Thread-0 0Thread-0 2Thread-0 4Thread-0 6main 3Thread-0 8main 5main 7main 9Thread-0 10Thread-0 12Thread-0 14Thread-0 16Thread-0 18Thread-0 20Thread-0 22Thread-0 24Thread-0 26Thread-0 28main 11main 13main 15main 17main 19main 21main 23main 25main 27main 29main 31main 33main 35main 37main 39Thread-0 30main 41main 43main 45main 47main 49main 51main 53main 55main 57Thread-0 32main 59main 61main 63main 65Thread-0 34Thread-0 36Thread-0 38Thread-0 40Thread-0 42Thread-0 44Thread-0 46Thread-0 48Thread-0 50Thread-0 52Thread-0 54Thread-0 56main 67Thread-0 58Thread-0 60Thread-0 62Thread-0 64Thread-0 66Thread-0 68main 69Thread-0 70Thread-0 72Thread-0 74Thread-0 76main 71Thread-0 78Thread-0 80Thread-0 82Thread-0 84main 73Thread-0 86Thread-0 88Thread-0 90Thread-0 92main 75Thread-0 94main 77main 79main 81main 83main 85Thread-0 96main 87main 89main 91Thread-0 98main 93main 95main 97main 99Thread-1 0Thread-1 2Thread-1 4Thread-1 6Thread-1 8Thread-1 10Thread-1 12Thread-1 14Thread-1 16Thread-1 18Thread-1 20Thread-1 22Thread-1 24Thread-1 26Thread-1 28Thread-1 30Thread-1 32Thread-1 34Thread-1 36Thread-1 38Thread-1 40Thread-1 42Thread-1 44Thread-1 46Thread-1 48Thread-1 50Thread-1 52Thread-1 54Thread-1 56Thread-1 58Thread-1 60Thread-1 62Thread-1 64Thread-1 66Thread-1 68Thread-1 70Thread-1 72Thread-1 74Thread-1 76Thread-1 78Thread-1 80Thread-1 82Thread-1 84Thread-1 86Thread-1 88Thread-1 90Thread-1 92Thread-1 94Thread-1 96Thread-1 98Process finished with exit code 0
问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。
问题二:如果再启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start().
方式2:实现Runnable接口
package vip.zdkk.java;/*** @author: zdkk* @create 2022-05-11 22:55** 创建多线程的方法2:实现Runnable接口* 1. 创建一个实现Runnable接口的类* 2. 实现类实现Runnable接口中的抽象方法run()* 3. 创建实现类的对象* 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象* 5. 通过Thread类的对象调用start()* 调用该Thread对象的run方法,而此run方法内调用了传给Thread类实例的Runnable类型的对象的run方法*/public class ThreadTest2 {public static void main(String[] args) {// 3. 创建实现类的对象MyThread2 m1 = new MyThread2();// 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象// 5. 通过Thread类的对象调用start(),实际调用的是该Thread类对象的run方法,然后在方法内部调用m1的run方法new Thread(m1).start();// 再启动一个线程new Thread(m1).start();}}// 1. 创建一个实现Runnable接口的类class MyThread2 implements Runnable {// 2. 实现类实现Runnable接口中的抽象方法run()@Overridepublic void run() {for (int i = 0; i < 100; i++) {if ((i % 2) == 0) {System.out.println(Thread.currentThread().getName() + " " + i);}}}}
第二种方式的优势:
- 类的继承更贴合实际,而不是通通继承Thread
- 共享数据不用写成
static类型
线程的调度
基于时间片的抢占式调度策略
线程的优先级等级:
MAX_PRIORITY = 10;MIN_PRIORITY = 1;NORM_PRIORITY = 5
默认是NORM_PRIORITY
高优先级的线程要抢占低优先级线程CPU的执行权。但只是从概率上讲,高优先级的线程高概率被执行。
