java多线程高并发
java多线程高并发
CAS
compare and swap
compare and exchange
没有锁的状态下可以保证多个线程对一个值更新,首先我们定义一个0,多线程一致的情况下来改这个值,首先我们读取当前值E=0,我们想把这个0改成1为,这个时候1写回原来的线程的时候要比较E和当前新值N是否相等。
- 相等更新为新值1(V)
- 不相等继续读取前新值(E)比较当前新值(N)
ABA问题
在CAS算法中,需要取出内存中某时刻的数据(由用户完成),在下一时刻比较并替换(由CPU完成,该操作是原子的)。这个时间差中,会导致数据的变化。
假设如下事件序列:
- 线程 1 从内存位置V中取出A。
- 线程 2 从位置V中取出A。
- 线程 2 进行了一些操作,将B写入位置V。
- 线程 2 将A再次写入位置V。
- 线程 1 进行CAS操作,发现位置V中仍然是A,操作成功。
尽管线程 1 的CAS操作成功,但不代表这个过程没有问题——对于线程 1 ,线程 2 的修改已经丢失。
简称你的女朋友跟你分手之后去找别的男朋友然后又跟你复合了这就是ABA问题
ABA问题解决思路
对值加入一个版本号A1.0,B1.1,A1.2
CAS在底层有一串汇编指令直接支持
lock cmpxchg指令
- 加了lock指令对值修改的时候其他CPU不不许打断。
硬件:
- lock指令在执行后面指令的时候锁定一个北桥信号
无锁、偏向锁、轻量级锁(无锁、自旋锁,自适应自旋)、重量级锁升级过程
synchronized优化的过程和markword信息相关,这个优化过程就是升级过程
Hotspot的实现
锁状态 | 25位 | 31位 | 1位 | 4bit | 1bit偏向锁位 | 2bit锁标志位 |
---|---|---|---|---|---|---|
无锁态(new) | unused | hashCode(如果有调用) | unused | 分代年龄 | 0 | 0,1 |
锁状态 | 54位 | 2位 | 1位 | 4bit | 1bit偏向锁位 | 2bit锁标志位 |
---|---|---|---|---|---|---|
偏向锁 | 当前现在指针JavaThread* | Epoch | unused | 分代年龄 | 1 | 0,1 |
锁状态 | 62位 | 2bit锁标志位 |
---|---|---|
轻量级锁,自旋锁,无锁 | 指向线程栈中Lock Record的指针 | 0,0 |
重量级锁 | 指向互斥量(重量级锁)的指针 | 1,0 |
GC标记信息 | CMS过程用到的标记信息 | 1,1 |
这些信息全部记录markword里面,markword有64个字节
锁升级过程表达
最开始的对象没有任何的锁加上来,然后我们调用的一个线程来了,没有一个人在坑里面干活前面有一个门,锁这个资源事向操作系统老大拿的有很多锁送给你,然后可以上一把大锁把门给锁了但是这样有很重量级。
所以在现在的synchronized优化的过程中事这样的,在拿到这把锁之后发现没有被任何人污染过,我是第一个拥有这个对象的线程那我就不上那么重量级的锁我就给他贴一个标签比如(杨龙)这就是偏向锁用完就释放,这个标签的意思就是整个的 markword(54位当前线程指针)。
当另一个线程争取同一个资源的时候撤销偏向锁,有竞争关系就会自动升级为轻量级锁,轻量级锁是Lock Record指针有一个自旋操作这就是CAS操作
当锁自旋到一定的状态比如自旋10次,等待的线程超过CPU的二分之一,这个时候就会升级位重量级锁,用户态执行一些特殊操作是要经过内核态来执行的来拿重量级锁(指向互斥量的指针),重量级有一个队列不消耗cpu。操作系统轮到你的就可以解冻拿到资源。