介绍
-
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博客