ReentrantLock中的Condition
1.ReentrantLock
,也被称为“可重入锁”,是一个同步工具类
Sync
类是 ReentrantLock
(可重入锁)中的一个 内部抽象类,它是 ReentrantLock
实现锁功能(比如加锁、解锁、可重入性等)的核心基础类,并使用了 Java 底层的 AQS(AbstractQueuedSynchronizer,抽象队列同步器) 来实现线程同步。
2. Condition 是什么?
Condition
是 java.util.concurrent.locks
包中的一个 接口,它代表了一个 条件变量,用于线程间的 协调与通信。
它提供了一组方法,类似于 Object
的 wait / notify / notifyAll
,但是更强大、更灵活,而且与 ReentrantLock
绑定使用。可以实现多个等待队列。
3.获取 Condition 的方式:
你不能直接 new 一个 Condition,而是通过 ReentrantLock
对象的 newCondition()
方法来创建:
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition(); // 创建一个条件变量
4. Condition 的使用示例(经典:生产者消费者)
场景:
-
有一个缓冲区(比如队列),容量有限
-
生产者往里放数据,如果满了,就等待
-
消费者从里拿数据,如果空了,就等待
-
使用 两个 Condition:
-
notFull
:表示“缓冲区不满”,生产者等待这个条件 -
notEmpty
:表示“缓冲区不空”,消费者等待这个条件
-
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ProducerConsumerWithCondition {private final Queue<Integer> queue = new LinkedList<>();private final int capacity = 5;private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition(); // 缓冲区不满private final Condition notEmpty = lock.newCondition(); // 缓冲区不空// 生产者public void produce(int item) throws InterruptedException {lock.lock();try {while (queue.size() == capacity) {System.out.println("队列已满,生产者等待...");notFull.await(); // 缓冲区满了,生产者等待 notFull 条件}queue.add(item);System.out.println("生产: " + item + ",队列大小 = " + queue.size());notEmpty.signal(); // 通知消费者,队列不空了} finally {lock.unlock();}}// 消费者public int consume() throws InterruptedException {lock.lock();try {while (queue.isEmpty()) {System.out.println("队列为空,消费者等待...");notEmpty.await(); // 缓冲区空了,消费者等待 notEmpty 条件}int item = queue.remove();System.out.println("消费: " + item + ",队列大小 = " + queue.size());notFull.signal(); // 通知生产者,缓冲区不满了return item;} finally {lock.unlock();}}
}
-
notFull
:当队列满了,生产者调用notFull.await()
进入等待,释放锁 -
notEmpty
:当队列空了,消费者调用notEmpty.await()
进入等待,释放锁 -
当有新的数据被生产出来,调用
notEmpty.signal()
唤醒等待的消费者 -
当有数据被消费,调用
notFull.signal()
唤醒等待的生产者
5.注意notFull.signal(); 与 lock.unlock();的区别
notFull.signal():
它仅仅是一个 “通知” 操作,告诉等待的线程可以尝试重新竞争锁了,但不会释放任何锁!
// 生产者
lock.lock();
try {while (queue.size() == capacity) {notFull.await(); // 缓冲区满了,生产者等待}queue.add(item);notEmpty.signal(); // 通知消费者:缓冲区非空了,可以消费了
} finally {lock.unlock(); // ✅ 必须要释放锁!
}
1. notFull.signal()
做了什么?
-
假如消费者消费了一个元素,队列不再满
-
消费者调用:
notFull.signal();
-
这个方法会 唤醒一个正在
notFull.await()
上等待的生产者线程 -
但注意:这个唤醒只是让生产者线程从等待队列移到了锁的竞争队列,它仍然需要获取锁之后才能继续运行!
2. lock.unlock()
做了什么?
-
生产者或消费者在完成自己的操作后,必须调用
lock.unlock()
-
这个方法会 释放当前线程持有的锁
-
一旦锁被释放,其他正在等待获取该锁的线程(比如刚被唤醒的生产者)就能获取锁并继续执行了