云计算百科
云计算领域专业知识百科平台

Java学习第一百二十六部分——锁

目录

一、前言简介

二、基础锁——synchronized关键字

三、高级锁——java.util.concurrent.locks包

三、锁的优化技术与实践建议

四、锁的设计原理与内存模型

五、锁的使用对比与选型建议

六、总结归纳


一、前言简介

       Java中的锁机制是实现线程同步、保证数据一致性和避免竞态条件的核心工具,主要分为基础锁(内置锁)和高级锁(java.util.concurrent.locks包)。

二、基础锁——synchronized关键字

1. 实现原理

  • 基于JVM内置的监视器锁(Monitor),通过对象头的Mark Word实现。

  • 通过字节码指令monitorenter和monitorexit控制锁的获取与释放。

2. 使用方式

  • 同步代码块:显式指定锁对

public void method() {
    synchronized (lockObject) {  // lockObject可以是任意对象
        // 临界区代码
    }
}

  • 同步实例方法:锁是当前实例(this)

public synchronized void method() {
    // 临界区代码
}

  • 同步静态方法:锁是当前类的Class对象

public static synchronized void staticMethod() {
// 临界区代码
}

3. 核心特性

  • 可重入性:同一线程可重复获取同一把锁。

  • 非公平锁:默认不保证等待线程的获取顺序。

  • 自动释放:线程执行完毕或异常时自动释放锁。

  • 阻塞等待:未获取锁的线程会进入阻塞状态(BLOCKED)。

4. 优缺点

  • 优点:简单易用,无需手动释放锁。

  • 缺点:功能单一(不支持超时、中断等)。

三、高级锁——java.util.concurrent.locks包

ReentrantLock(可重入锁)

1.核心特性:

  • 可重入性:同synchronized。

  • 公平性选择:支持公平锁(new ReentrantLock(true))和非公平锁(默认)。

  • 尝试获取锁:tryLock()(立即返回)和tryLock(long timeout, TimeUnit unit)(超时等待)。

  • 可中断:lockInterruptibly()响应中断。

  • 条件变量:通过newCondition()创建Condition对象,实现精细等待/通知。

2.示例代码:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

lock.lock();  // 手动加锁
try {
    while (conditionNotMet) {
        condition.await();  // 释放锁并等待
    }
    // 临界区代码
    condition.signal();  // 唤醒等待线程
} finally {
    lock.unlock();  // 必须手动释放锁
}

ReentrantReadWriteLock(读写锁)

1.核心思想:读写分离,提升读多写少场景的性能。

读锁(共享锁):允许多个线程同时读。

写锁(独占锁):只允许一个线程写(且排斥所有读/写)。

2.示例代码:

ReadWriteLock rwLock = new ReentrantReadWriteLock();

// 读操作
rwLock.readLock().lock();
try {
    // 并发读操作
} finally {
    rwLock.readLock().unlock();
}

// 写操作
rwLock.writeLock().lock();
try {
    // 独占写操作
} finally {
    rwLock.writeLock().unlock();
}

锁降级:允许从写锁降级为读锁(防止其他写锁插入)。

StampedLock(邮戳锁,Java 8+)

1.三种模式:

  • 写锁(Writing Lock):独占锁,类似ReentrantLock。

  • 悲观读锁(Pessimistic Reading):类似读写锁的读锁。

  • 乐观读(Optimistic Reading):无锁机制,读时不阻塞写操作。

2.优势:性能更高(尤其读多写少场景),但API复杂。

3.示例代码:

StampedLock lock = new StampedLock();

// 乐观读
long stamp = lock.tryOptimisticRead();
// 读操作…
if (!lock.validate(stamp)) {  // 检查是否有写操作
    stamp = lock.readLock();  // 升级为悲观读锁
    try {
        // 重新读数据
    } finally {
        lock.unlockRead(stamp);
    }
}

// 写锁
long stamp = lock.writeLock();
try {
    // 写操作
} finally {
    lock.unlockWrite(stamp);
}

三、锁的优化技术与实践建议

  • ​​锁消除​​:检测无竞争代码,移除同步操作。

  • ​​锁粗化​​:合并相邻同步块,减少锁开销。

  • ​​自适应自旋​​:根据竞争动态调整自旋次数,避免线程阻塞。

  • ​​避免死锁​​:按固定顺序获取锁,或使用tryLock()超时机制。

  • ​​减小锁粒度​​:同步范围最小化(如用ConcurrentHashMap分段锁)。

  • 短任务 → synchronized(自动优化)。

  • 高竞争写入 → 悲观锁。

  • 低冲突计数 → CAS原子类。

四、锁的设计原理与内存模型

  • ​​Happens-Before规则​​:锁释放操作对后续加锁操作可见,确保内存一致性。

  • ​​AQS框架​​:核心是volatile int state和FIFO队列,通过CAS管理竞争线程(如ReentrantLock实现)。

五、锁的使用对比与选型建议

特性synchronizedReentrantLockReentrantReadWriteLockStampedLock
可重入 ✅(但需注意模式)
公平锁 ❌(仅非公平) ✅(可配置) ✅(可配置)
尝试获取锁 ✅(tryLock())
超时等待
可中断
条件变量 wait()/notify() ✅(Condition)
读写分离 ✅(更灵活)
乐观读
锁降级
  • 优先使用 synchronized:简单场景下,JVM对synchronized有持续优化(如锁升级:无锁→偏向锁→轻量级锁→重量级锁)。

  • 需要高级功能时选 ReentrantLock:如超时、中断、公平锁、条件变量等。

  • 读多写少场景用读写锁:ReentrantReadWriteLock或StampedLock(后者性能更优,但API复杂)。

  • 避免锁嵌套:防止死锁(使用tryLock超时机制)。

  • 锁粒度最小化:减少竞争(如用ConcurrentHashMap代替synchronizedMap)。

  • 注意:StampedLock不是可重入锁,且没有条件变量,需谨慎使用乐观读模式。

六、总结归纳

       Java中基础锁(synchronized)简单易用但功能有限,高级锁(如ReentrantLock、读写锁、StampedLock)提供更灵活的并发控制(如公平性、超时、读写分离),需根据场景选择。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Java学习第一百二十六部分——锁
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!