Java同步机制详解:从synchronized到ReentrantLock
Java同步机制实现:从内置锁到显式锁
1. 内置锁与监视器机制
synchronized关键字
synchronized
是Java最基础的同步机制,它通过内置锁(Intrinsic Lock)实现线程同步:
// 同步方法
public synchronized void increment() {
count++;
}
// 同步代码块
public void increment() {
synchronized(this) {
count++;
}
}
实践建议:
- 优先使用同步代码块而非同步方法,缩小锁范围
- 避免在
synchronized
块中执行耗时操作 - 不要使用字符串常量作为锁对象(可能导致意外锁竞争)
对象头与锁升级
Java锁的状态会随着竞争情况升级:
锁状态说明:
- 偏向锁:Mark Word记录线程ID,适合单线程重复访问场景
- 轻量级锁:通过CAS自旋尝试获取锁
- 重量级锁:依赖操作系统互斥量,线程进入阻塞状态
实践建议:
- 对于短暂同步场景,可通过
-XX:-UseBiasedLocking
关闭偏向锁 使用
jol-core
工具查看对象头信息:// 添加依赖:org.openjdk.jol:jol-core System.out.println(ClassLayout.parseInstance(obj).toPrintable());
2. 显式锁工具
ReentrantLock
ReentrantLock
提供了比synchronized
更灵活的锁控制:
Lock lock = new ReentrantLock();
try {
lock.lock();
// 临界区代码
} finally {
lock.unlock(); // 必须手动释放
}
高级特性:
- 可中断锁:
lockInterruptibly()
- 尝试获取锁:
tryLock()
- 公平锁:
new ReentrantLock(true)
实践建议:
- 总是将
unlock()
放在finally
块中 - 对于需要超时控制的场景使用
tryLock(long timeout, TimeUnit unit)
- 公平锁会降低吞吐量,仅在严格顺序需求时使用
ReadWriteLock
读写分离锁显著提升读多写少场景的性能:
ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 读锁
rwLock.readLock().lock();
try {
// 多个线程可并发读取
} finally {
rwLock.readLock().unlock();
}
// 写锁
rwLock.writeLock().lock();
try {
// 独占写入
} finally {
rwLock.writeLock().unlock();
}
实践建议:
- 读写锁适合读操作远多于写操作的场景
- 注意写锁降级(获取写锁→获取读锁→释放写锁)的特殊用法
- 避免读锁内进行写操作,可能导致死锁
性能对比与选型
特性 | synchronized | ReentrantLock | ReadWriteLock |
---|---|---|---|
锁获取方式 | 自动 | 手动 | 手动 |
可中断 | 否 | 是 | 是 |
公平锁 | 否 | 可选 | 可选 |
条件变量 | 有限 | 灵活 | 灵活 |
锁降级 | 不支持 | 不支持 | 支持 |
性能(低竞争) | 高 | 中 | 中 |
性能(高竞争) | 低 | 高 | 高 |
通用实践原则:
- 简单同步场景优先使用
synchronized
- 需要高级功能时选择
ReentrantLock
- 读多写少场景使用
ReadWriteLock
- 考虑使用
StampedLock
(Java8+)获得更好的乐观读性能
常见问题解决方案
死锁预防:
// 使用tryLock避免死锁
if (lock1.tryLock()) {
try {
if (lock2.tryLock()) {
try {
// 成功获取两个锁
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
条件变量使用(生产者-消费者模式):
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
// 生产者
lock.lock();
try {
while (queue.isFull()) {
notFull.await();
}
queue.add(item);
notEmpty.signal();
} finally {
lock.unlock();
}
// 消费者
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await();
}
item = queue.remove();
notFull.signal();
} finally {
lock.unlock();
}
通过合理选择同步机制,可以构建出既安全又高效的并发程序。在实际开发中,建议结合性能测试工具(如JMH)验证不同方案的实际效果。