偏向锁是jvm为了消除无竞争状态下同步原语开销而引入的一种优化机制,其核心做法是让第一个获取锁的线程在对象头中记录自己的id,后续该线程再次进入时只需检查id是否匹配,无需执行cas操作。具体实现依赖于jvm启动参数中的-xx:+usebiasedlocking(java 15后默认关闭),当多个线程交替访问同一资源且无竞争时,它能显著减少上下文切换和cpu指令消耗。

偏向锁如何识别并锁定当前线程
偏向锁通过修改对象头中的Mark Word来记录持有锁的线程ID,从而避免后续的加锁动作。在Java 8及早期版本中,这是默认的优化手段。当线程A第一次访问同步块时,JVM会使用CAS操作将对象头中的Thread ID设置为A的ID,并设置偏向标志位为1。此后,只要没有其他线程尝试竞争这把锁,线程A再次进入该同步块时,JVM只需比对当前线程ID与对象头中的ID是否一致,若一致则直接继续执行,整个过程不涉及任何原子操作或系统调用。

public class BiasedLockDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// 模拟偏向锁生效:单线程反复获取锁
Thread t1 = new Thread(() -> {
for (int i = 0; i <p>上述代码展示了单线程场景下偏向锁的行为。由于只有一个线程T1不断获取同一个对象的锁,JVM会在首次获取时进行偏向操作,之后的9999次获取都将跳过CAS比较,直接获得锁。这证明了在无竞争环境下,偏向锁确实消除了同步开销。注意,如果此时启动第二个线程T2去竞争同一个锁,偏向锁会被撤销,并升级为轻量级锁。</p><h2>偏向锁在遇到竞争时的升级过程</h2><p>当有其他线程尝试获取已被偏向上的锁时,偏向锁机制会失效,锁状态会升级为轻量级锁。这一过程涉及全局安全点(Safepoint)的暂停和Mark Word的修改。一旦检测到竞争,JVM会停止所有拥有该偏向锁的线程,并将对象头中的Thread ID清空,恢复为正常的锁标记。随后,新竞争的线程会通过CAS操作尝试获取锁,如果成功,则进入轻量级锁阶段;如果失败,则可能进一步膨胀为重量级锁。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20dee630206.jpg" alt="JavaScript 开发专家" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家" class="overflowclass">JavaScript 开发专家</a>
<p class="overflowclass">掌握包含 ES6+、异步模式和 Node.js API 的现代 JavaScript。处理 Promise、事件循环和浏览器/Node 兼容性。在 JavaSc</p>
</div>
<a rel="nofollow" href="/xiazai/skill12680" title="JavaScript 开发专家" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><p><img src="https://img.php.cn/upload/article/001/242/473/179453755872428.png?x-oss-process=image/resize,p_40" alt="Java synchronized 中 偏向锁是什么"></p><pre class="brush:php;toolbar:false;">public class LockUpgradeDemo {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
// 第一阶段:单线程偏向
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("T1 holds biased lock");
}
});
t1.start();
t1.join();
// 第二阶段:引入竞争,触发升级
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("T2 competes for lock");
}
});
// 短暂延迟确保t1已完成偏向
Thread.sleep(100);
t2.start();
t2.join();
System.out.println("Lock upgraded to lightweight or heavier.");
}
}
在这个例子中,T1首先获取了偏向锁。当T1结束后,T2开始竞争。由于对象已经被偏向于T1,T2的CAS操作会失败(或者JVM在检测到竞争后主动撤销偏向)。此时,锁的状态不再保持偏向,而是根据竞争情况升级为轻量级锁(自旋)或重量级锁(OS Mutex)。开发者可以通过-XX:-UseBiasedLocking禁用此功能,强制使用轻量级或重量级锁,这在某些高并发多核场景下可能更稳定,因为偏向锁的撤销成本较高。
为什么Java 15默认关闭了偏向锁
基于程序中偏向锁撤销的具体行为,我们发现其在多核高并发环境下可能导致性能下降而非提升。偏向锁的设计初衷是优化单线程或极少竞争的场景,但在现代服务器应用中,多线程共享资源的竞争较为常见。一旦竞争发生,JVM必须暂停相关线程以撤销偏向,这个“全局安全点”操作带来了显著的延迟。此外,偏向锁的撤销逻辑复杂,且在许多实际基准测试中,其带来的收益往往被撤销成本抵消。因此,从Java 15开始,Oracle决定默认关闭偏向锁,以减少不必要的复杂性并提高整体系统的可预测性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










