使用通俗易懂的语言讲述多线程领域最重要的概念。
多线程概念初识分为四个小节。
- 线程与进程
- 串行、并行、并发
- 多线程与高并发
- 同步/异步、阻塞/非阻塞
进程和线程
操作系统是包含多个进程的容器,每个进程又都是容纳多个线程的容器。
Oracle官方文档中的定义
https://docs.oracle.com/cd/E19455-01/806-5257/6je9h032b/index.html
- 进程:通过fork(2)系统调用创建的UNIX环境(例如文件描述符,用户ID等),它被设置为运行程序。
- 线程:在进程上下文中执行的一系列指令。
什么是进程
进程的英文是Process,指的是程序的一次执行。在用户下达运行程序的命令后,就会产生进程。
以王者荣耀游戏为例,程序安装到手机中,占用2GB左右磁盘空间。平时不允许游戏时,不会耗电、内存、CPU,因为它是一堆代码和资源放置在磁盘中,没有执行。游戏启动后,程序才会变成进程,占用内存,消耗CPU等。进程可以视作对代码的实例化,每次运行的进程也不尽相同。
Windows资源管理器展示的就是已经运行起来的进程。
总结:进程是程序(我们写的代码)的真正运行实例,是资源分配的基本单位。
什么是线程
线程是CPU的基本调度单位,每个线程执行的都是进程代码的某个片段。
演示实例:通过任务管理器监视运行Java程序后的线程数变化
public static void main(String[] args) {
for(int i = 0; i < 200; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
执行代码创建200个线程时,系统新增一个java.exe进程,线程是227个。
情景解释:我们将房间视为1个进程,而房间中的2个人视为两个线程。房间有很多自己的属性,例如15平方、床、电视等,房间并没有做任何事情,真正做事是房间中的2个人。人在房间中可以看电视睡觉,可以比喻进程中的线程执行不同功能的代码。因此,进程是线程的容器,线程利用资源,执行代码,实现所需的效果。
进程和线程的不同
进程与线程有着相似的生命周期,都有着就绪、等待、终止等状态。不同之处有很多。
1. 起源不同
先有进程,后有线程。CPU处理速度远远快于内存等外设的速度,线程的出现时为了提高CPU的利用率和程序的执行效率。
2. 概念不同
进程是具有独立功能程序的一次运行,是系统分配资源和调度的单位,而线程是CPU的基本调度单位。
3. 内存分享方式不同
操作系统会给每个进程分配一定的内存,但进程之间内存是不共享的。进程之间通信需要通过IPC(进程间通信)实现。进程内部线程可以访问共享内存,不需要额外处理。
4. 拥有资源不同
线程是进程的一部分。线程共享的内容包括(1)进程代码段;(2)进程的公有数据(便于线程之间通信);(3)进程打开的文件描述符;(4)信号的处理器;(5)进程的当前目录;(6)进程用户ID与进程组ID。
线程独有的内容包括(1)线程ID;(2)寄存器组的值;(3)线程的堆栈;(4)错误返回码;(5)线程的信号屏蔽码。
5. 数量不同
一个进程可以有多个线程,但至少有一个线程。
6. 开销不同
- 线程的创建、终止时间比进程短
- 同一进程的线程切换时间比进程切换短
- 同一进程的各个线程间共享内存和文件资源,可以不通过内核进行通信。
Java语言与多线程的渊源
Java设计之初就已经支持多线程,经常用于服务端开发。
此外,Java语言的多线程是一对一映射到操作系统的内核线程。有些语言的线程是虚拟线程,不会在操作系统中对应建立一个线程。例如上面的例子,我们通过代码创建200个线程,实实在在的在操作系统中创建了200个线程。
debug运行简单的Java程序,可以注意JVM启动了多个核心线程。。
public static void main(String[] args) {
System.out.println("Hello Threads");
}
JVM核心线程介绍
- main:主线程,用户程序的入口
- Signal Dispatcher(信号分配器):把操作系统发送给JVM的信号分发给适当的处理程序。
- Finalizer(终结器):负责调用对象的finalize()方法。
- Reference Handler :和GC、引用相关的线程。该线程是有着最高优先级的守护线程,排队待处理的References。GC创建一个简单的待处理引用链接列表,该线程将它们快速添加到适当的队列中,并通知ReferenceQueue监听器。
- Attach Listener(附加监听器):在目标JVM上动态附加一个监听器线程,在第一次附加请求时启动。该线程实际上允许另一个进程注入线程在运行的JVM中查询有关JVM运行的某些详细信息,例如调试、IDE debug等。
- DestroyJavaVM:该线程在程序退出时卸载JVM,大多情况下处于等待状态。
参考资料: 1. 慕课网课;2. https://stackoverflow.com/questions/19427339/jvm-core-threads