在Java中,加锁的过程是随着线程竞争的激烈程度而改变的。总体过程如下:
偏向锁–》轻量级锁–》重量级锁
在这里我们首先要了解Java对象的保存:
1、Java对象的保存
Java对象保存在内存的时候分三个部分:对象头,示例数据,对齐填充字节
而对象头又分为三个部分,其中的MarkWord就是专门用来保存和记录所有与锁有关的信息的
可以看到markword共有32个字节用来记录所得信息。
同时,线程中还会有一个monitor(监视器锁),用来记录线程的锁获取。
可以看到,owner是用来记录当前获取锁的线程的。EntryQ存放了获取锁失败被阻塞的线程。Nest记录了重入锁的个数Candidate则记录了下一个要抢锁的线程。
2、抢锁过程:
偏向锁:锁会偏向第一次拿到锁的线程——只有一个线程(竞争最小)
(1)monitor中的owner字段 设置成拿到锁的线程ID
(2)改变markword中的值 线程ID 是否为偏向锁的字段 当作这个线程已将拿到锁了
(3)当这线程如果第二次进入的时候并且此时没有别的线程进来过,通过markword中的线程ID字段判断与前一个线程ID是一样的,就不再加锁,直接进行代码过程
当第二个线程进来(竞争逐渐增强)的时候:轻量锁(先调用CAS看第二个线程能不能抢锁成功,能成功就把偏向锁线程ID改成当前线程,不成功如下:)
(1)当前这个要获取锁的线程会创建一个 锁记录lock record
(2)对象的markword中的指向轻量级锁的指针会指向lock record markword中的标记位变为00
(3)lock record中的owner字段会保存这个对象的markword的地址
(4)改变monitor中的owner字段
当两个以上的线程竞争(也不一定只是两个线程以上,此处表达的就是竞争已经十分激烈的时候)的时候:重量级锁
(1)monitor中的owner字段 设置成拿到锁的线程ID
(2)对象的markword中的结构会变成重量级锁,指向重量级锁的指针会指向monitor中的EntryQ所关联的互斥锁(系统级)——(用户态到内核态)
(3)系统互斥锁(mutex)会阻塞住没有获取到锁的线程(没有抢到锁的v线都将被阻塞住)
以上就是锁的升级过程
用CAS的操作就是为了防止,当一个线程的monitor
3、优化锁的方法:
1、缩小锁的粒度
2、删除不必要的加锁
3、自旋锁:(并不是一把锁,而是对获取不到锁的线程的处理控制)因为把线程直接到阻塞态,十分耗费资源(用户态到内核态的切换),或者刚到阻塞就又可以竞争了,所以就选用了自选锁的处理方式。让没有拿到锁的线程都处于whlie(flage);当竞争很激烈的时候,多个线程自旋,CPu就会判断,把许多自旋的变到阻塞,就节省了CPU运行资源
Java多线程之锁的升级
最新推荐文章于 2022-07-25 11:31:40 发布