Synchronized的花花肠子

10 篇文章 0 订阅
6 篇文章 0 订阅

源于蚂蚁课堂的学习,点击这里查看(老余很给力)

对于久经沙场的Java老手来说,锁的概念一定如雷贯耳。毕竟,在打赢并发和多线程的战役中,锁可谓举足轻重。伴随技术领域的推进,系统架构逐渐演变为微服务,锁的范围超脱单个JVM的管理,分布式锁开始替代传统JVM锁,崭露头角。凭借自身的灵活和高逼格的技术栈,收到大家的一致追捧。但由浅入深,JVM锁不意味着过时与淘汰,相反,它在众多业务中还是那么不可替代。JVM锁的家族中,Synchronized一骑红尘,广为流传。本文,帝都的雁就来为读者盘点Synchronized的那些花花肠子。(PS:需要有Java对象布局的技术基础)

一、基本概念 

Synchronized,即同步的概念,属Java关键字。C++语言编写,方便Java自动调用,常年游际于线程安全问题,自动管理线程访问对象锁的上锁和释放。

二、Synchronized的上锁方式

Synchronized上锁是建立在任意Java对象上的,也就是说,使用者必须明确通知Synchronized,到底要锁什么对象。特别注意:保证线程安全的前提,锁的对象必须是同一个! 

 1.this锁

 这种锁的对象属于隐式,即调用者本身。如下两种:

public synchronized void a(){}

public void b(){
	synchronized (this){}
}

2.object锁 

 将锁定义一个object类型对象,多个线程共享这个对象。

private Object lock = new Object();

public void a(){synchronized (lock){}}

 3.类的字节码锁

定义静态的成员变量或者直接使用当前类字节码当做锁。 

public class Test02 {
    private static Object lock = new Object();
    public void a(){synchronized (lock){}}
    public void b(){synchronized (Test02.class){}}
}

由上可知,其实多线程上锁,不关注锁的对象是谁,而是关注多个线程是不是抢一把锁。相信大家在网上一定看到过这样的对比,如果把上锁这件事情比喻为开车,那么Synchronized就是自动挡,Lock是手动挡。可以这么理解,但Synchronized自动上锁和撤销的原理可不是那么简单的。 

三、Monitor(检察者) 

C++为Synchronized提供了一个Monitor对象,Monitor维护Synchronized对Java对象上锁的开销。

Monitor有几个比较好的功能:

1.锁重入次数 

了解重入前,先上一段代码。 

public synchronized void a(){
    b();
}

public synchronized void b(){}

 当前线程获取a方法上的对象锁后,再去访问b方法,不会继续开一个对象锁,而通过锁的重入性,进行传递。

如果使用汇编指令查看的话,会发现,在进入a方法时触发MonitorEnter指令,Monitor开始检察锁,Monitor的重入次数会+1;进入b方法,也会触发MonitorEnter指令,使得Monitor的重入次数会继续+1;退出b方法时,触发MonitorExit指令,Monitor的重入次数-1;退出a方法时,触发MonitorExit指令,Monitor的重入次数-1,此时重入数为0,Monitor撤销锁。

2.锁的持有线程

Owner,Monitor就将当前获取Java对象锁的线程ID记录在自身的属性中,用于做后续的判断。 

 3.等待池

WaitSet,即被当前锁对象使用wait()方法阻塞,释放CPU执行权的线程,会全部放入等待池,等待唤醒。 

4.锁池

没有抢到对象锁的线程,会被让如锁池,等对象锁的释放后,锁池中的线程会重新进行锁的竞争。 

 5.锁竞争

 Cxq,也就是多个线程抢夺Java对象锁的过程。

四、锁的升级 

JDK6之后,Synchronized做了很大的优化。试想一些场景:如果某个上了锁的业务在一段时间内只被一个线程频繁访问,那么有必要加锁吗?再者,如果两个线程先后访问上锁业务,前者执行时间特别快,后者可能只需要等候忽略不计的时间,那么这时候通过锁的方式让后者进入阻塞,然后再唤醒,是否多余? 结合这些场景,Synchronized提出了锁的一些概念。

 1.偏向锁

偏向,即对某个线程有好感,先到先得。

当锁被一个线程获取后,Java锁对象的对象头中一个名为Mark World的空间会记录下来此线程的ID,并且将自身偏向锁的状态由0变为1,并使用Monitor对业务加锁。

该线程再次来访问时,发现对象锁的偏向锁状态为1,就会去比较线程ID是否是自己,如果是自己则没有Monitor什么事,不做拦截,直接访问。

那如果不是呢?轻量锁开始登场。

2.轻量锁

 本质上不会使当前线程进入阻塞。

当发现锁中偏向线程ID不是自己时,当前线程会把锁对象的Mark World拷贝至自身独享的栈帧空间中,然后通过CAS(compareAndSwap)尝试修改对象锁中Mark World的引用。修改成功,表明前面的线程执行结束,释放了锁,那么当前线程执行业务,并调整偏向锁的信息。

3.重量锁

 轻量锁首次自旋失败后,不会马上让当前线程进入阻塞状态,而且继续自旋。当超过配置的自旋次数后还不超过,就会停止这种消耗CPU的行为,将其阻塞等待,锁也由轻量级变为重量级。

欢迎大家和帝都的雁积极互动,头脑交流会比个人埋头苦学更有效!共勉!

公众号:帝都的雁

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值