当前位置:网站首页>偏向锁/轻量锁/重级锁锁锁更健康,上锁解锁到底是怎么完成实现的
偏向锁/轻量锁/重级锁锁锁更健康,上锁解锁到底是怎么完成实现的
2022-06-30 20:10:00 【澎湖Java架构师】
轻量级锁升级重量级锁
- 只有一个线程抢夺时JVM上偏向锁
- 在出现一个线程时就是轻量级锁。轻量级锁通过CAS进行上锁。失败则会发生自旋
- 当自旋大一定程度或者,此时又出现一个线程上锁,此时会切换成重量级锁。
class Heavy{ } public class HeavyLock
{
public static void main(String[] args) throws InterruptedException
{
Heavy heavy = new Heavy();
final Thread t1 = new Thread(new Runnable()
{
@SneakyThrows
@Override
public void run()
{
synchronized (heavy)
{
System.out.println("t1:"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
final Thread t2 = new Thread(new Runnable()
{
@SneakyThrows
@Override
public void run()
{
synchronized (heavy)
{
System.out.println("t2"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
final Thread t3 = new Thread(new Runnable()
{
@SneakyThrows
@Override
public void run()
{
synchronized (heavy)
{
System.out.println("t3"+ClassLayout.parseInstance(heavy).toPrintable());
}
TimeUnit.SECONDS.sleep(1000000);
}
});
t1.start();
TimeUnit.SECONDS.sleep(2);
t2.start();
t3.start();
TimeUnit.SECONDS.sleep(5);
System.out.println(ClassLayout.parseInstance(heavy).toPrintable());
}
}
- 我们能够发现,在出现两个线程进行轻量级锁争抢的时候会切换成重量级锁。

都是重量级锁的同时,我发现其他信息是固定的,这个也和我们在内存布局中一样,这里指向的是锁指针,对应的是C++中ObjectMonitor对象指针。重量级指针内部有一个队列就是将没抢上的线程挂起用的。所以这里不会变化。这里注意和线程id进行区分
- 重量级锁用完之后就会释放掉成无锁状态。

锁操作
- 上面我们分别介绍了偏向锁,轻量级锁,重量级锁的定义及场景切换。这里我们稍微总结下三种锁使用场景
- 偏向锁在JVM开启偏向条件下,默认是偏向锁
- 当有另外一个线程再次对对象进行加锁时,不管有没有发生竞争都是轻量级锁。只不过发生竞争时会通过CAS抢占,CAS一定次数还是失败则升级重量级锁。而偏向锁切换到轻量级锁的过程是先撤销偏向锁在上轻量级锁
- 当有两个及以上的时候发生竞争就会切换成重量级锁。切换之前会有一个线程通过自旋等待。默认是10次。这是不固定的,因为有自适应自旋锁的存在
- 除了锁之间的关系,我们还需要掌握下各个锁的原理,上锁解锁的全过程
- 偏向锁 上锁 首先我们检测下状态位是否是101。如果是在看下当前markword存储的threadId是否是当前线程。如果是当前线程则可以执行当前代码块 如果不是当前线程,则通过CAS写入当前线程;如果写入成功则说明偏向锁上锁成功,继续执行当前代码块 如果CAS写入失败,则说明有资源抢占。偏向锁升级成轻量级锁。

### 解锁- 上面的代码案列中,我也有所提到偏向锁是不会主动释放的。因为我们在还没加锁的时候默认就是偏向锁。只有发生竞争了升级轻量级锁的时候才会撤销偏向锁。- 所以说偏向锁的解锁就是不操作。我们这里主要说下在升级轻量级锁是关于偏向锁的撤销的逻辑

## 轻量级锁### 上锁- 轻量级锁上锁过程需要借助一个`Lock Record` ; 他是存储在线程栈帧中的一块内存地址。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mc7pHsa9-1656577825850)(https://upload-images.jianshu.io/upload_images/28037261-93f03f9e95b44e93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
- 因为偏向锁不需要释放,他的可重入式锁就是不做任何操作。但是在轻量级锁中涉及到释放锁。那么轻量级锁如何体现可重入式呢?;- 在上图中如果是无锁或者偏向锁会在线程栈中开辟LockRecord来存储markword的地址叫做`Displaced MardWord`- 但是在已经是偏向锁的条件分支里,我没有体现可重入式的概念。在这条分支线里实际上是检测到markword中指向的是当前栈帧的时候JVM还是会开辟一个`Lock Record`,也不会写回到markword中。`LockRecord`不会存储原本markword的内容。此时的`LockRecord`本身就是一个计数器的功能。在释放重入式锁的时候也只是删除`LockRecord`而不会去操作markword。- 为什么JVM这样设计呢?因为这样就避免了每次轻量级锁的开销### 解锁- 对于轻量级锁,其性能提升的依据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果打破这个依据则除了互斥的开销外,还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢;

总结
- synchronized作为被人诟病的一个设计,在JDK1.6之后真的改版了,不要在拿之前的眼光看待他了。毕竟偏向锁和轻量级锁的引入已经将性能提升了很多了。
- 偏向锁默认进行,节省调度时间
- 轻量级锁通过CAS完成等待,省调线程挂起,唤醒等操作
- 重量级将线程串行化,保障了线程之间的并发
- 三种状态锁循序渐进给我们适配不同程度的并发需求

原
边栏推荐
猜你喜欢

项目经理是领导吗?可以批评指责成员吗?

PHP require/include differences

Evolution of screen display technology

谈谈内联函数

《大厂面试》之JVM篇21问与答

二叉查找树(一) - 概念与C语言实现

Analysis of breakpoint continuation and download principle

B_QuRT_User_Guide(31)

Label Contrastive Coding based Graph Neural Network for Graph Classification

A complete collection of vulnerability scanning tools. Mom doesn't have to worry that I won't find any more vulnerabilities
随机推荐
网上炒股开户安全嘛!?
Lvalue reference and lvalue reference
哈夫曼樹(一)基本概念與C語言實現
To eliminate bugs, developers must know several bug exploration and testing artifacts.
PHP require/include differences
北京大学ACM Problems 1005:I Think I Need a Houseboat
How do I get the largest K massive data
Heartbeat uses NFS to make MySQL highly available based on CRM
北京大学ACM Problems 1003:Hangover
DEX文件解析 - method_ids解析
杰理之触摸按键识别流程【篇】
亚马逊在阿拉伯联合酋长国限制LGBTQ相关的搜索和产品销售
obsidian配合hugo的使用,让markdown本地编辑软件与在线化无缝衔接
以全栈全功能解决方案,应对多样工具复杂环境DevOps落地难题
Huffman tree (I) basic concept and C language implementation
PHP require/include 区别
大神詳解開源 BUFF 增益攻略丨直播
Lumiprobe生物素亚磷酰胺(羟脯氨酸)说明书
Jerry's question about long press boot detection [chapter]
第299场