JAVA多线程
2021-08-25 10:53:45 2 举报
JAVA多线程相关图例: 锁升级等
作者其他创作
大纲/内容
store
工作内存
偏向线程ID是否为空
指向栈中锁记录的指针
进入同步块边界
对象的hashcode
tail != null
0
shouldParkAfterFailedAcquire()
自旋超时
CAS获取Monitor的Owner
自旋等待获取锁
FairSync#lock()
hasQueuedPredecessors()是否需要排队
state
返回node
N
分代年龄
偏向锁
线程1
23
可偏向锁:偏向锁: 1锁标志位:01
CAS更新偏向线程ID
线程3
公平锁
EPOCH
Blocked
重置 偏向锁|锁标志位: 0|01
获取失败
成功获取
GC
偏向锁:适用于单线程适用锁的情况,如果线程争用激烈,那么应该禁用偏向锁。轻量级锁:适用于竞争较不激烈的情况(这和乐观锁的使用范围类似)重量级锁:适用于竞争激烈的情况
需要排队返回True
对象头
True
MarkWord
是否偏移?
ReentrantLock的加锁流程公平非公平
1
NonfairSync
tryAquire()
node加入到队尾
NonFairSync#lock()
锁状态
从 JDK1.7 开始,自旋锁默认启用,自旋次数由 JVM 设置决定,这里我不建议设置的重试次数过多,因为 CAS 重试操作意味着长时间地占用 CPU。自旋锁重试之后如果抢锁依然失败,同步锁就会升级至重量级锁,锁标志位改为 10。在这个状态下,未抢到锁的线程都会进入 Monitor,之后会被阻塞在 _WaitSet 队列中。
锁升级:这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率
新一轮轻量级锁的竞争
自旋
FairSync
偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行)
2
失败
ReentrantLock的静态内部类
执行同步块
修改为 0 | 01
获取成功
AbstractQueuedSynchronizer
锁已经被占用
线程2
获取到锁
填充对齐
指令重排特性
保持缓存一致的协议 MESI
0 1
acquireQueued()
用户线程
当一个线程访问同步代码块/同步方法时
本质:对 线程 - 工作内存 - 主存 之间进行数据读写访问的过程 进行抽象,然后构建的模型目的:屏蔽OS和各种硬件之间的内存访问差异,让Java程序在各种平台下都有一致的访问效率
反复尝试
阻塞之前获取锁的机会?非公平锁:3次公平锁:2次
tryAcquire()
enq构造队列,并将node加入到队列
ByteBuffer.allocate(capability)第一种方式是分此JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较馒
1. 可运行-阻塞-可运行 存在上下文的切换2. synchronized是基于悲观锁的,当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
synchronized结束
实例数据
False未获取到锁
获取到Monitor
无锁
重入数+1
非公平锁
False
存在锁竞争时
进入阻塞列表,等待唤醒
队列中,在阻塞之前获取一次锁,若可以获取锁,那么就不需要进行阻塞(也就不需要进行上下文切换);若获取失败,则放入队列(FIFO)本次锁的获取满足:当前节点 == Head.next 才能进行tryAcquire()
获取到锁当前节点设置为头节点(该节点不进行排队)
CAS获取锁
1. 高并发的时候,会影响CPU的效率(CAS操作不成功的话,会导致一直自旋,CPU的压力会很大。)2.只能保证一个变量的原子操作3. ABA问题
tryAcquire()返回True
自旋等待
False
主存
锁释放
返回 Node
tryAcquire()返回False
分为公平锁和非公平锁的情况:公平锁:是否需要排队,不需要排队则使用CAS获取锁,否则进行排队(进入等待队列)非公平锁:直接CAS去获取锁
1. 八大基本操作都是原子操作2. volatile:线程每次对工作内存中变量的操作都要同步到主存中3. 保证执行结果一致的情况下,对指令进行重排,以提高效率
设置锁的持有线程为当前线程
null
执行完成
轻量级锁
自旋超时升级为重量级锁
开始
state == 0
NonFair
hasQueuedPredecessors()
Y
指向当前线程ID
当前线程队列中可以获取到锁的线程,不需要排队,返回False
assign
4
CAS
撤销偏向锁
True获取到锁
CAS还原markword
write
CAS更新MarkWord指向锁记录指针
lock()
1 1
可偏向?
三大特性:
锁升级简化过程
执行同步代码块
GC分代年龄
重量级锁
JMM
True需要排队
失败,有竞争
锁标志位设置为: 00
False不需要排队
JVM堆内存
线程n
...
指向重量级锁指针
对象要求 8 bit的整数倍
32 bit的对象头
数组长度
ParkAndCheckInterrupt()线程进入阻塞状态
锁标志位(11)
可见性
addWaiter
head.next.thread== 当前线程
Fair
锁标志位
缓存一致性协议
CAS更新
队列只有一个元素(头结点 = 获取到锁的线程),则不需要排队返回False
synchronized开始
栈帧中建立Lock Record,存储当前MarkWord的拷贝
再次循环判断
唤醒那些被挂起的线程
use
state + 1
类指针
释放锁
轻量级锁/自旋锁
参考:https://blog.csdn.net/weixin_40910372/article/details/107726978https://www.processon.com/view/5ef83ec65653bb2925b99219?fromnew=1https://segmentfault.com/a/1190000022904663
参考:https://blog.csdn.net/lsgqjh/article/details/63685058https://www.processon.com/view/5e29b0e8e4b04579e40c15a7?fromnew=1源码
队列中元素数目> 1
1 0
偏向锁标志
占用锁的线程是否当前线程
直接物理内存
25
阻塞结束
偏向锁?
unlock
偏向锁撤销
线程ID
锁升级
当前节点的前节点是否是head节点
将线程打包成节点,放在等待队列中
队列是否初始化?
read
CAS尝试获取锁
存在竞争,升级为轻量级锁
锁标志位(00)
是否重入
Sync
ByteBuffer.allocateDirect(capability)第2种方式是分派OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快
Fair 或 NonFair
synchronized
lock
load
只有数组对象,对象头中才有数组长度信息
JavaBean
0 0
八大基本操作
False再次循环判断
自旋超时(默认 10 次)
进入队列之前:非公平锁会进行抢占;进入队列之后,非公平锁与公平锁一致,都要排队
恢复线程
未初始化等待队列,返回False表示不需要排队
释放monitor,释放锁
内存模型
唤醒等待线程
OutOfMemoryError: Direct buffer memory
原子性
CAS设置monitor的Owner
获取锁
synchronized关键字
被唤醒
全局安全点
acquire()
0 条评论
下一页