Synchoronized

介绍

  • Synchronzied 是一个java内置锁,属于悲观锁,悲观锁每次读写都会加锁。

    • JDK6之前,是基于Monitor机制实现的,依赖于底层互斥原话Mutex,属于重量级锁,性能较低

    • JDK6之后,迫于JUC包的压力,对synchronzied进行了优化,增加锁升级、锁消除、锁粗化等机制,sync为了避免阻塞直接从用户态切换到内核态park线程,所以默认情况下是开启偏向锁,当然也可以禁用偏向锁,这样锁升级就会从无锁开始。

用法

  • 锁方法:

public synchronized void synchronizedMethod() {
    // 同步代码块
}
  • 同步代码块:

public void someMethod() {
    // 其他代码
    synchronized (lockObject) {
        // 同步代码块
    }
    // 其他代码
}

从并发的三大特性看synchoronized

  • 原子性:

    • 通过 加锁 和 解锁 保证。

  • 可见性:

    • 在 Java 中,可见性指的是当一个线程修改了共享变量的值后,其他线程能够立即看到这个修改。synchronized 通过 内存屏障 来确保可见性。在加锁时,synchronized 使用 load 屏障和 acquire 屏障,这样可以保证在获取锁之前,所有的读操作都能看到最新的值。在解锁时,synchronized 使用 store 屏障和 release 屏障,这样可以保证在释放锁之后,所有的写操作都对其他线程可见。

  • 有序性:

    • 在多线程环境下,指令可能发生重排,导致代码的执行顺序与预期不符。synchronized 通过内存屏障来防止指令重排,从而保证了有序性。在加锁时和解锁时,synchronized 使用了 内存屏障,确保了代码的执行顺序按照程序的顺序进行。

从字节码层面看synchoronized

  • 同步代码块:

    • 通过一个monitorenter和两个monitorexit实现的,对于第一个monitorexit处理正常返回,第二个monitorexit是处理异常情况自动释放锁的,synchronized不像Lock需要自己手动释放锁,不过也可以通过Unsafe类去手动控制synchronized,不过不推荐。

  • synchronized可重入锁的实现:

    • synchronized会维护一个计数器,当monitorenter时计数器+1,monitorexit时计数器-1。

锁升级

  • 没有禁用向锁 以及 满足延迟偏向条件:

    • 偏向锁(Biased Locking):在程序开始时,会经过一段延迟(通常为4秒),然后创建锁对象。在此期间,该对象处于匿名偏向状态。当第一个线程访问同步代码块时,会使用 CAS 操作将当前线程的 ID 记录到锁对象的对象头中的 MarkWord,并将锁状态设为偏向锁。下次该线程再访问该同步代码块时,只需检查对象头的线程 ID 是否与当前线程 ID 相同,无需进行 CAS 自旋获取锁,从而提高性能。

    • 轻量级锁(Lightweight Locking):只有当多个线程竞争锁时,偏向锁会自动撤销,升级为轻量级锁。轻量级锁使用自旋来尝试获取同步代码块,而不会阻塞线程,从而避免了从用户态切换到内核态的开销。

    • 自适应自旋(Adaptive Spinning):JVM 在轻量级锁基础上引入了自适应自旋技术。根据之前自旋成功的概率来动态调整自旋次数。如果自旋成功率较高,JVM 将增加自旋次数以提高获取锁的成功率;如果自旋成功率较低,JVM 将减少自旋次数,避免浪费处理器资源。

    • 重量级锁(Heavyweight Locking):当锁竞争非常激烈时,轻量级锁可能会升级为重量级锁。重量级锁会阻塞线程,涉及用户态和内核态的切换,因此重量级锁的性能开销较大。

  • 禁用偏向锁 或 不满足延迟偏向条件:

    • 无锁状态:初始时,对象处于无锁状态。没有任何线程持有或竞争该对象的锁。

    • 轻量级锁升级:当发生轻微锁竞争时,无锁状态的对象会升级为轻量级锁。

      • CAS 操作:JVM 使用 CAS(Compare and Swap)操作尝试将对象的 MarkWord 修改为指向锁记录的指针。

      • 拷贝锁记录:如果 CAS 操作成功,JVM 会将原来的 MarkWord 拷贝一份到线程的栈帧中的锁记录中。这样,线程在栈帧中拥有了自己的锁记录,用于记录锁的状态和其他信息。

    • 轻量级锁解锁到无锁状态:当持有轻量级锁的线程释放锁时,锁会回退到无锁状态。

    • 重量级锁升级:如果发生激烈竞争,轻量级锁无法成功获取锁时,会升级为重量级锁。

      • 创建 Monitor 对象:JVM 会创建一个 Monitor 对象用于管理锁的状态和线程的等待。

      • CAS 操作:JVM 使用 CAS 操作将对象的 MarkWord 修改为指向 Monitor 对象的指针。

    • 重量级锁解锁到无锁状态:当持有重量级锁的线程释放锁时,锁会回退到无锁状态。

锁粗化、锁消除

  • 锁粗化:

    • 当锁粗化出现在循环体中,当JVM检测到这种操作时,会将锁范围扩大到循环体外。

  • 锁消除:

    • 加了锁但不存在竞争,JVM会把锁消除。

这部分内容是通过阅读理解了柚子哥的文章:枫吹过的柚-CSDN博客

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值