Java同步机制实现:从内置锁到显式锁

1. 内置锁与监视器机制

synchronized关键字

synchronized是Java最基础的同步机制,它通过内置锁(Intrinsic Lock)实现线程同步:

// 同步方法
public synchronized void increment() {
    count++;
}

// 同步代码块
public void increment() {
    synchronized(this) {
        count++;
    }
}

实践建议

  • 优先使用同步代码块而非同步方法,缩小锁范围
  • 避免在synchronized块中执行耗时操作
  • 不要使用字符串常量作为锁对象(可能导致意外锁竞争)

对象头与锁升级

Java锁的状态会随着竞争情况升级:

图1

锁状态说明

  • 偏向锁: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(); // 必须手动释放
}

高级特性

  1. 可中断锁:lockInterruptibly()
  2. 尝试获取锁:tryLock()
  3. 公平锁: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();
}

实践建议

  • 读写锁适合读操作远多于写操作的场景
  • 注意写锁降级(获取写锁→获取读锁→释放写锁)的特殊用法
  • 避免读锁内进行写操作,可能导致死锁

性能对比与选型

特性synchronizedReentrantLockReadWriteLock
锁获取方式自动手动手动
可中断
公平锁可选可选
条件变量有限灵活灵活
锁降级不支持不支持支持
性能(低竞争)
性能(高竞争)

通用实践原则

  1. 简单同步场景优先使用synchronized
  2. 需要高级功能时选择ReentrantLock
  3. 读多写少场景使用ReadWriteLock
  4. 考虑使用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)验证不同方案的实际效果。

添加新评论