首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >ReentrantLock 与 Condition

ReentrantLock 与 Condition

原创
作者头像
兰亭集
发布2026-04-26 10:29:38
发布2026-04-26 10:29:38
230
举报
文章被收录于专栏:软件工程软件工程

ReentrantLock 可重入锁

使用示例:

代码语言:java
复制
public void accessMutex() {
    Lock lock = new ReentrantLock();
    lock.lock();
    try {
        // access
    } finally {
        lock.unlock();
    }
}

Condition

  • await方法:业务条件不满足,释放锁,线程进入等待状态
  • signal方法:业务条件满足,唤醒等待状态下的线程,唤醒后的线程会先再获取锁,获取锁后继续原逻辑的执行

使用示例:

代码语言:java
复制
public class ConditionExample {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean businessCondition = false;

    public void waitCondition() throws InterruptedException {
        lock.lock();
        try {
            while (!businessCondition) {
                condition.await();  // 业务条件不满足,线程进入等待状态
            }
        } finally {
            lock.unlock();
        }
    }
    
    public void meetCondition() {
        lock.lock();
        try {
            businessCondition = true;
            condition.signal();  // 唤醒等待条件的线程
        } finally {
            lock.unlock();
        }
    }
}

加锁原理

ReentrantLock内部类FairSync和NonFairSync继承了AbstractQueuedSynchronizer实现了公平锁和非公平锁,

  • 公平锁 - FairSync:等待时间最长的线程优先获取锁
  • 非公平锁 - NonFairSync:不管等待队列中是否有等待锁的线程,新线程直接尝试获取锁,抢占锁成功则先执行互斥逻辑 Condition的一个实现类ConditionObject也在AbstractQueuedSynchronizer中,具体锁机制参考AbstractQueuedSynchronizer的实现。

ReentrantLock、Condition 与 synchronized、wait/notify 的对比

比较\类型

synchronized, wait/nofify

ReentrantLock, Condition

加锁性能

synchronized底层依赖了操作系统的Mutex,JVM对synchronized做了包括轻量锁在内的锁优化,但当锁多线程频繁竞争锁时,OS Mutex的使用涉及到内核态与用户态的频繁切换,性能上不如ReentrantLock

使用CAS加锁,加锁主要逻辑在用户态中处理,频繁锁竞争下性能也较好

条件等待

在锁对象上调用wait/notify,可读性不好;无法区分不同业务条件

一个锁可对应多个业务条件,可读性更好;每个业务条件都单独对应一个等待队列,控制更精准

代码实现

synchronized代码实现简洁明了,加锁逻辑简单、竞争较少的场景下优先使用synchronized

锁竞争频繁、有Condition的使用时,优先使用ReentrantLock

示例:多线程生产和消费场景下线程安全的消费队列 - MyBlockingQueueV2

代码语言:java
复制
public class MyBlockingQueueV2<T> {

    private Object[] data;
    private int getIndex;
    private int putIndex;
    private int cnt;

    private final Lock lock;
    private final Condition notFull;
    private final Condition notEmpty;

    public MyBlockingQueueV2(int size) {
        this.data = new Object[size];
        this.lock = new ReentrantLock();
        this.notFull = lock.newCondition();
        this.notEmpty = lock.newCondition();
    }

    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (cnt == data.length) {  // 队列满,等待消费。线程唤醒后需重新检查条件是否满足
                notFull.await();
            }
            data[putIndex] = item;
            putIndex = nextIndex(putIndex);
            cnt++;
            notEmpty.signal();  // 唤醒等待的消费线程
        } finally {
            lock.unlock();
        }
    }

    public T get() throws InterruptedException {
        lock.lock();
        try {
            while (cnt == 0) {  // 队列空,等待生产。线程唤醒后需重新检查条件是否满足
                notEmpty.await();
            }
            Object item = data[getIndex];
            getIndex = nextIndex(getIndex);
            cnt--;
            notFull.signal();  // 唤醒等待的生产线程
            return (T) item;
        } finally {
            lock.unlock();
        }
    }

    private int nextIndex(int curIndex) {
        return (curIndex + 1) % data.length;
    }

}

详细完整逻辑可以参考JDK的ArrayBlockingQueue.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ReentrantLock 可重入锁
  • Condition
  • 加锁原理
  • ReentrantLock、Condition 与 synchronized、wait/notify 的对比
  • 示例:多线程生产和消费场景下线程安全的消费队列 - MyBlockingQueueV2
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档