synchronize实现原理及锁升级

在刚接触jdk5的时候,网上都说能尽量不实用synchronize就不用,同时有替代方案,比如轻量级锁,locks,但是在jdk6之后对synchronized有了优化。

应用场景

既然引入了同步锁的概念,那啥时候可以用?当然是处在共享资源且有多个线程可能对其操作的情况。比如定义了一个全局变量,由多个线程对其进行+1操作,如果让不同的线程来执行,在操作时获取到的值可能是旧的,这就会导致最终的结果跟预期不一致,这时我们就需要保证其安全性。

既然说到保证安全性,那么肯定会对程序执行的效率产生影响,毕竟这是一个互斥的属性,synchronized在其中寻求折中点,所以出现了:无锁->偏向锁->轻量级锁->重量级锁。
synchronized实用的位置还是没有变,分别在:普通方法、静态方法、普通方法块中。


/**
 * synchronized用法
 */
public class TestSyn {
    public synchronized void  get(){}

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

    public static synchronized void put(){}
    
    public void put2(){
        synchronized (TestSyn.class){}
    }
}

其中,1和2 、3和4具有相同的作用,如果是对于用.class作为锁,那么就会产生互斥作用,因为synchronized内部通过监视器(monitorenter、monitorexixt)指令进出锁,当使用.class,全局就只有一个,那么就会产生资源竞争。

所以在产生资源竞争时,又不好让未获得锁的线程直接阻塞,及节省后续的恢复线程切换上下文所需的资源开销,jdk引入了上述的4种锁类型。首先无锁肯定是效率最快的,平常我们工作使用的,大部分是这种被定义为private的属性。

升级流程

首先进入锁时,会调用cas方法,把mark word记录的线程id改成当前线程,且偏移锁标志位为1,epoch改成01,如果修改成功,则执行方法体内容。

如果当前有线程获取到偏移锁且线程未结束时,其他线程想通过CAS操作修改锁对象头的id失败时,锁会立刻进行升级操作,变成轻量级锁。如果其他线程调进入同步方法,且当前拥有偏移锁的线程isalive返回false时,cas操作会成功执行,此时则不会升级。

升级为轻量级锁时,有两种规则来获取锁,一种是自旋操作,另外一种是自适应自旋操作。其中自旋锁可以通过调整jvm参数preBlockSpin,默认次数是10。另外一种自适应自旋锁,是根据最近一次获得该对象锁耗费的自选次数来进行调整,可能多也可能少,如果没有成功获取过,可能会跳过该步骤。在当前阶段,默认的一种场景就是,获取锁操作能立刻返回。如果此时还没获取到锁, 那么会升级成重量级锁,进行阻塞,在当前占用锁释放资源后,再进行资源竞争,重量级锁使用monitor监视器来实现,借助monitorenter和monitorexixt指令对其他线程进行阻塞和唤醒

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值