【典中典】进程和线程
进程概念
进程是程序的一次执行过程,是系统运行程序的基本单位 => 进程是动态的。
系统运行一个程序就是一个进程从创建、运行到消亡的过程。
线程概念
和进程相似,但线程是一个比进程更小的执行单元。一个进行在其执行的过程中可以产生多个线程。
同类的多个线程共享进程的堆、方法区,每个线程有独有的程序计数器、虚拟机栈和本地方法栈 => 操作系统产生线程、切换线程是比较廉价的 => 线程被称为轻量级线程。
Java程序是天生的多线程程序,使用JMX查看Java程序的线程:
public class MultiThread { |
输出结果:
10:52:30.614 [main] INFO com.juzi.juc.jmx.MultiThread - [5] Monitor Ctrl-Break |
由此可知:一个Java程序的运行是main线程和其他线程同时运行。
进程和线程的关系、区别、优缺点
「JVM运行时的内存结构」
从上图可以看出:一个进程可以存在多个线程,多个线程共享进程的堆、方法区,每个线程独享PC寄存器、虚拟机栈、本地方法栈。
=> 线程是更小的运行单元。进程和线程最大的区别在于各进程基本上是独立的,各线程之间会相互影响;线程执行系统开销小,但不利于资源的管理和保护,进程则刚好相反。
PC寄存器(程序计数器)为什么被设定为私有?
多线程在一个特定的时间段内只会执行其中的某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常性的中断和回复,为了保证分毫不差,同时为了能够准确的记录各个线程正在执行的当前字节码指令地址,最好的办法就是为每一个线程都分配一个PC寄存器,这样依赖各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
虚拟机栈和本地方法栈为什么是私有的?
1)虚拟机栈:每个Java方法执行前就会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息,从方法调用开始至执行结束的过程,对应着一个栈帧在Java虚拟机栈中入栈和出栈的过程
2)本地方法栈:和虚拟机栈相似,区别是虚拟机栈为虚拟机执行字节码指令,本地方法栈执行native方法
=> 为了保证线程中的局部变量不被别的线程访问,虚拟机栈和本地方法栈都是线程私有的。
【典中典】 并行&&并发、同步&&异步
并发和并行的区别
- 并行:多个作业在同一时刻执行
- 并发:多个作业在同一时间段内执行
同步和异步的区别
- 同步:发出一个调用后,等待结果返回再执行之后的操作
- 异步:调用发出后,不等待结果的返回
理解线程安全和不安全
- 线程安全:在多线程环境下,程序能够正确地处理并发访问共享资源的能力。如果一个程序在多线程环境下能够保证数据的正确性和一致性,那么它就是线程安全的。
- 线程不安全:在多线程环境下,程序不能正确地处理并发访问共享资源的情况。在多线程环境下,如果多个线程同时访问同一个共享资源,而且没有进行同步控制,那么就可能会出现数据竞争、死锁等问题,导致程序出现错误或崩溃。
线程的生命周期
在Thread源码中,线程的状态被分为了NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED六种状态。
public enum State { |
NEW
:初始状态,线程被创建出来,尚未调用start()
方法RUNNABLE
:运行状态,线程调用了start()
方法,等待运行的状态BLOCKED
:阻塞状态,等待锁释放WAITING
:等待状态,表示该线程需要等待其他线程发出 通知或中断 操作TIME_WAITING
:超时等待状态,可以在指定时间后自行返回,而不是像WAITING状态那样一直等待TERMINATE
:终止状态,表示该线程已经运行结束
PS:整个线程的生命周期中,线程并不是固定处于某一个状态,而是随着程序的执行在不同的状态之间切换的
从上图可以看出,线程创建之后处于NEW状态,在调用start()
方法之后开始执行,此时线程处于READY状态,在获得CPU时间片之后就处于RUNNING状态。
为什么JVM没有区分RUNNING和READY两种状态?
Thread#state注释原话:These states are virtual machine states which do not reflect any operating system thread states.
RUNNABLE状态的注释:Thread state for a runnable thread. A thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.
(https://www.zhihu.com/question/56494969/answer/154053599 By Dawell)现在的时分(time-sharing)多任务(multi-task)操作系统架构通常都是用所谓的“时间分片(time quantum or time slice)”方式进行抢占式(preemptive)轮转调度(round-robin式)。这个时间分片通常是很小的,一个线程一次最多只能在 cpu 上运行比如10-20ms 的时间(此时处于 running 状态),也即大概只有0.01秒这一量级,时间片用后就要被切换下来放入调度队列的末尾等待再次调度。(也即回到 ready 状态) 通常,Java的线程状态是服务于监控的,如果线程切换得是如此之快,那么区分 ready 与 running 就没什么太大意义了。
简单来说就是这两个状态之间切换过于频繁,时间太短,区分这两种状态没有多大意义。
当线程执行wait()
方法之后,线程就进入WAITING状态,进入等待状态的线程需要依靠其他线程的通知才能够返回RUNNABLE状态。
TIME_WAITING状态相当于在WAITING状态的基础上增加了超时限制。在超时时间结束后,线程自行返回到RUNNABLE状态。
在线程进入同步代码方法/块或者调用wait()
方法(被notify()
)重新进入同步代码方法/块,但是此时锁被其他线程占有,那么这个时候线程将进入BLOCKED状态。
线程执行完run()
方法之后将会进入到TERMINATED状态。
参考文章
- https://javaguide.cn/java/concurrent/java-concurrent-questions-01.html
- https://blog.csdn.net/TZ845195485/article/details/109211695
- https://mp.weixin.qq.com/s/UOrXql_LhOD8dhTq_EPI0w