Java 锁机制
synchronized
在 jdk1.6 之后,synchronized
原语通过引入偏向锁、轻量锁进行了优化。
synchronized 是通过 monitor_enter
和 monitor_exit
来控制的,通过 javap 反编译能观察到。
线程在进入 synchronized 代码块时,将通过获取对象头内 Mark Work
的锁标记来决定是否进入 临界区。
偏向锁
没有多线程竞争环境,如果没有设置 -XX:-UseBiasedLocking
关闭偏向锁的话将会通过 CAS 获取锁,并设置持有锁的线程 id,当该线程再次尝试获取锁时,如果锁内线程为当前线程则直接判断获取锁标记。
轻量锁
在多个线程尝试获取偏向锁时,当线程获取偏向锁失败时将会膨胀为轻量锁,通过CAS操作来获取锁标记进入临界区。
重量锁
如果轻量锁的 CAS 操作失败时,将会升级为重量级锁。
重量锁依赖底层的 Mutex Lock
总线锁,需要将用户态切换为内核态,成本较大。
重入锁
在 synchronized
偏向锁和 ReentrantLock
中都设计了重入锁,
自旋锁
当多线程环境下,线程获取锁标记或立马释放时,自旋锁就有其意义。
在 Java 源码的AbstractQueuedSynchronizer
中,线程的出现竞争时就是先是尝试进行自旋锁,之后再进行锁升级。
自旋锁消除了用户态切换至内核态的上下文切换,但是也增加cpu的消耗。
锁降级
在jvm进入安全点时,会检测空闲的 monitor
并对其进行锁降级以提高下次获取锁时的效率。