synchronized 底层实现由jvm 保证,
在jvm 运行过程中,可能出现 偏向锁,轻量级锁,重量级锁 (锁的重量依次加重)
偏向锁 实现是在 先确认偏向锁标识是可获得状态,第一次获取到锁的时候,将对象的 mark word 中的偏向锁线程的标识为自己,下一次进入到同步块的时候,直接获取锁。 当有其他线程竞争锁的时候,发现 markword 并不是自己,会尝试进行一次 cas 替换,如果不成功,就会将锁升级为轻量级锁 消耗:极少
轻量级锁 实现: 在执行同步块之前,jvm 会先在当前线程的栈帧中创建用于存储所锁记录的空间,并将对象头中 markWord 复制到锁记录中,。然后线程尝试使用 cas 将对象头中的 mark word 替换为指向锁记录的指针,如果成功,当前线程获得锁,如果失败,说明以有竞争,当前线程会使用自旋来获取锁。自旋获取锁失败,会将对象头的锁标识改为重量级指针。 解锁的时候,会使用原子 Cas 操作将 Displaced Mark Word 替换为对象头,成功说明没有发生竞争, 如果失败,说明当前锁存在竞争,锁会膨胀为重量级锁。
消耗: 复制和自旋
重量级锁:
Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。JDK中对Synchronized做的种种优化,其核心都是为了减少这种重量级锁的使用。JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”。
reentrantLock 的实现:
reentrantLock基于 AQS 实现。AQS 内部通过对 volatile 的 state 读写以及cas 操作 和在某些条件下让线程进入阻塞状态实现。
volatile 消耗小于 synchronized ,reentrantLock 中某些条件下让线程进入阻塞状态 的消耗 可能与 synchronized 相当。
因此 性能 偏向锁 > 轻量级锁 > reentrantLock > synchronized
而偏向锁 和轻量级锁 能够启用是在特殊条件下的。
本文由博客群发一文多发等运营工具平台 OpenWrite 发布