《一文说透数据结构》系列之什么是堆?看这一篇就够了
1、锁升级的过程
当多个线程同时竞争一个对象监视器时:当前对象结构中的mark word中是否是当前线程id,如果是则当前线程获得偏向锁。
如果不是,则通过CAS将当前线程id置换到mark word中,如果成功则获得偏向锁,如果不成功则说明有竞争,升级为轻量级锁。
后续再通过CAS将线程的指针放到mark word中,若成功则获得锁,否则升级为自旋锁。自旋锁仍然为轻量级锁,不成功升级为重量级锁。
对象结构:在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充
mark word:存在于对象头中,存储对象的hashcode,锁标识,分代年龄及GC标识等信息
由上图可以看出,为何偏向锁是将线程id放入mark word,轻量级锁为何将锁的指针放入mark word。
偏向锁:指偏向于第一个访问的线程,在运行过程中,同步锁没有竞争,则会在这个线程的头部加一个标志位,标记为偏向锁,如果发生竞争则会升级为轻量级锁或者重量级锁
自旋锁:线程请求不到对象锁时不会堵塞,只是自己循环一下等待对象锁的释放。因为线程的堵塞和唤醒非常消耗内存,所以自旋锁可以很好的优化这个问题。
但它只适合等待时间比较短的,而且并发量不高的场景。
maven报错:Return code is: 501 , ReasonPhrase:HTTPS Required
2、升级到重量级锁后,如何运行
当多线程竞争时,不满足的条件的线程会进入同步队列,满足条件后进入同步代码。在同步代码中执行wait方法,释放对象锁,进入右侧等待队列,当唤醒时 还需要再次获得互斥锁。
synchronized结构:
Contention List:竞争队列,所有请求锁的线程首先被放在这个竞争队列中;
Entry List:Contention List中那些有资格成为候选资源的线程被移动到Entry List中;
Wait Set:哪些调用wait方法被阻塞的线程被放置在这里;
OnDeck:任意时刻,最多只有一个线程正在竞争锁资源,该线程被成为OnDeck;
Owner:当前已经获取到所资源的线程被称为Owner;
大量并发线程会在contention List中,然后将有资格成为候选的放到entry list中。调用的wait的线程放到wait set中,当被唤醒后会放到entry list中。
指定EntryList中的某个线程为OnDeck线程(一般是最先进去的那个线程),然后onedeck线程去竞争锁,但是此时其他未进入contention list的线程会先自旋一下看是否能获得到锁,
所以说synchronied不是公平的。
当使用synchronized加类锁时,会有严重的效率问题,此时需要考虑是否可以修改为细粒度锁,当修改细粒度锁时,要避免死锁。
上述若有不对,麻烦各位指正
喀山之旅丨俄罗斯喀山交换小结