## Java并发编程实战:深入剖析synchronized与Lock的底层实现原理
synchronized关键字底层实现机制
synchronized是Java中最基本的同步机制,它通过JVM内置的监视器锁(Monitor)实现线程同步。在字节码层面,synchronized通过monitorenter和monitorexit两个指令来实现,分别对应锁的获取和释放操作。当线程进入同步代码块时,会尝试获取对象的监视器锁,成功获取后其他线程将被阻塞,直到锁被释放。
对象头与Mark Word
每个Java对象在内存中都由对象头、实例数据和对齐填充组成。对象头中的Mark Word是 synchronized 实现的关键,它存储了对象的哈希码、分代年龄和锁标志位等信息。在32位JVM中,Mark Word长度为32位,根据不同锁状态会存储不同的内容。
锁升级过程
为了平衡性能与安全性,Java 6引入了锁升级机制,包含四种状态:无锁状态、偏向锁、轻量级锁和重量级锁。当没有竞争时,使用偏向锁减少同步开销;当有轻微竞争时,升级为轻量级锁(自旋锁);当竞争激烈时,最终升级为重量级锁,此时未获取锁的线程将进入阻塞状态。
Lock接口的底层实现原理
Lock接口提供了比synchronized更灵活的锁操作,其典型实现类ReentrantLock基于AbstractQueuedSynchronizer(AQS)框架构建。AQS使用一个FIFO队列来管理获取锁失败的线程,并通过一个volatile修饰的int类型state变量来表示同步状态。
AQS核心机制
AQS采用模板方法模式,提供了acquire()和release()等模板方法,子类通过重写tryAcquire()和tryRelease()方法来实现具体的同步语义。当线程尝试获取锁时,如果state为0表示锁可用,通过CAS操作将其设为1并设置当前线程为独占者;如果获取失败,则将线程封装成节点加入等待队列。
公平锁与非公平锁
ReentrantLock支持公平锁和非公平锁两种模式。公平锁严格按照FIFO顺序分配锁,避免了线程饥饿但性能较低;非公平锁允许插队,新请求的线程可以直接尝试获取锁,提高了吞吐量但可能导致某些线程长时间等待。
synchronized与Lock的性能对比
在低竞争环境下,synchronized由于JVM的优化(如锁消除、锁粗化、自适应自旋等)性能与Lock相当甚至更优;在高竞争环境下,Lock提供了更细粒度的控制和更好的性能,特别是Condition接口提供了更灵活的等待/通知机制。
适用场景分析
synchronized适用于简单的同步场景,代码简洁且自动释放锁,不易出错;Lock适用于需要高级功能(如尝试非阻塞获取锁、可中断获取锁、超时获取锁等)的复杂同步场景,提供了更好的灵活性和扩展性。
底层内存语义与可见性保证
两者都通过内存屏障保证可见性和有序性。synchronized通过MonitorEnter和MonitorExit指令建立happens-before关系;Lock通过volatile变量state的读写和UNSAFE的屏障方法实现类似效果,确保锁释放前的操作对后续获取锁的线程可见。
728

被折叠的 条评论
为什么被折叠?



