并发基础
1、线程的生命周期
new -就绪(start)-运行(获得cpu)-结束(异常或执行完)
运行时,被中断,进入就绪,被唤醒获取cpu资源进入运行
2、线程结束:run方法执行完成;抛出没有捕获的异常;
JAVA程序退出:无非守护线程
3、中断
interrupt:中断线程,向需要中断的线程打个招呼
isInterrupted:线程检查自己是否被中断
静态方法:Thread.interrupted() 中断标志位复位,获取当前调用线程的中断标志位
JAVA 只有协作式任务,没有抢占式
4、处理不可中断的阻塞
主要用于IO操作,解决办法是调用IO的释放资源方法
常用方法
1、sleep不会释放锁,阻塞线程,可中断,不考虑线程优先级。低优先级可以参与CPU资源的竞争
2、wait: wait 使用时必须先获取对象锁,即必须在 synchronized 修饰的代码块中使用,会释放锁
3、yield 不会释放锁,不阻塞线程, 当前线程主动让出CPU占有权,当前线程变成可运行状态等待CPU资源,只能使同优先级或更高优先级的线程有执行的机会
4、join:等待调用join方法的线程结束之后,程序再继续执行
5、volatile
1、保证此变量对所有线程的可见性。但是操作并非原子操作
2、禁止指令重排序优化:通过插入内存屏障保证一致性。
JAVA线程的本质:轻量级线程
6、并发三要素
原子性
可见性
有序性
7、线程关键字以及实现原理
WAIT :使当前线程阻塞, 方法需要获取锁,一般在同步代码块,会释放锁
NOTIFY:唤醒等待的线程,方法需要获取锁
NOTIFYALL:唤醒等待的线程,方法需要获取锁
sleep:休眠,不会释放锁,可以不用写在同步代码块
LOCK:基于AQS(一个队列加volidate 的状态变量 再加cas)
7.1、synchronized 原理:
image.png
对象头里面存储有无锁,偏向锁,轻量级锁,重量级锁的标志,以及当前持有锁的线程
7.1.1、synchronized修饰代码块
基于monitorentry和monitorexit指令实现
7.1.2、synchronized修饰方法
由方法调用指令来读取运行时常量池中的ACC_SYNCHRONIZED标志隐式实现的,如果方法表结构(method_info Structure)中的ACC_SYNCHRONIZED标志被设置,那么线程在执行方法前会先去获取对象的monitor对象,如果获取成功则执行方法代码,执行完毕后释放monitor对象,如果monitor对象已经被其它线程获取,那么当前线程被阻塞
7.1.3、锁升级
锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态(级别从低到高)
image.png
偏向锁的升级:当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。
轻量级锁的升级
如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。
但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。
锁可以升级不可以降级,但是偏向锁状态可以被重置为无锁状态。
CAS:
AQS:
公平锁和非公平锁
可重入锁和不可重入锁
信号量
CountDownLatch:阻塞当前调用线程,数量达到了某个点之后计数结束,才能继续往下走。
CyclicBarrier:阻塞当前调用线程,数量达到了某个点之后计数结束,才能继续往下走。不过可以循环使用
ArrayBlockingQueue:阻塞顺序队列
happen-before原则
(1)程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
(2)监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
(3)volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
(4)传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
(5)start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
(6)Join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
(7)程序中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断时间的发生。
(8)对象finalize规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。
8、中断
isInterrupted:是否中断
interrupt:中断
interrupted:静态方法,是否中断且清空中断标志位
————————————————
版权声明:本文为CSDN博主「发奋小青年」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ma_chen_qq/article/details/82990603
————————————————
版权声明:本文为CSDN博主「tongdanping」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tongdanping/article/details/79647337