《Java中使用Condition实现线程通信》
在Java多线程编程中,线程间的通信与同步是核心问题之一。传统的`synchronized`关键字结合`wait()`和`notify()`方法可以实现基本的线程协作,但存在灵活性不足、无法精准控制唤醒特定线程等问题。Java 5引入的`java.util.concurrent.locks.Condition`接口,通过与`Lock`配合使用,提供了更细粒度的线程控制能力,成为解决复杂线程通信场景的利器。
一、Condition的核心概念
Condition是`Lock`接口的配套组件,用于替代`Object`的`wait()`/`notify()`机制。其核心优势在于:
- 多条件队列:一个`Lock`对象可关联多个`Condition`,实现更复杂的线程分组唤醒
- 公平性控制:支持公平/非公平锁模式下的条件等待
- 中断响应**:提供`awaitInterruptibly()`等支持中断的方法
典型使用模式:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (条件不满足) {
condition.await(); // 释放锁并等待
}
// 执行临界区操作
condition.signal(); // 唤醒一个等待线程
} finally {
lock.unlock();
}
二、Condition的核心方法解析
1. **等待方法**:
-
await()
:释放锁并进入等待状态,直到被唤醒 -
awaitUninterruptibly()
:忽略中断的等待 -
await(long time, TimeUnit unit)
:超时等待
2. **唤醒方法**:
-
signal()
:唤醒一个等待线程 -
signalAll()
:唤醒所有等待线程
3. **与Object方法对比**:
Object方法 | Condition方法 | 特点 |
---|---|---|
wait() | await() | 自动释放锁 |
notify() | signal() | 单线程唤醒 |
notifyAll() | signalAll() | 多线程唤醒 |
三、经典应用场景实现
1. 生产者-消费者模型
传统实现存在虚假唤醒问题,Condition方案更可靠:
class BoundedBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[100];
private int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
2. 线程池任务调度
实现带条件的任务队列:
class ConditionTaskQueue {
private final Queue tasks = new LinkedList();
private final Lock lock = new ReentrantLock();
private final Condition hasTasks = lock.newCondition();
public void addTask(Runnable task) {
lock.lock();
try {
tasks.add(task);
hasTasks.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public Runnable takeTask() throws InterruptedException {
lock.lock();
try {
while (tasks.isEmpty()) {
hasTasks.await(); // 等待任务
}
return tasks.poll();
} finally {
lock.unlock();
}
}
}
3. 读写锁优化实现
自定义读写锁中的条件控制:
class CustomReadWriteLock {
private int readers = 0;
private int writers = 0;
private int writeRequests = 0;
private final Lock lock = new ReentrantLock();
private final Condition readCondition = lock.newCondition();
private final Condition writeCondition = lock.newCondition();
public void lockRead() throws InterruptedException {
lock.lock();
try {
while (writers > 0 || writeRequests > 0) {
readCondition.await();
}
readers++;
} finally {
lock.unlock();
}
}
public void unlockRead() {
lock.lock();
try {
readers--;
if (readers == 0 && writeRequests > 0) {
writeCondition.signal();
}
} finally {
lock.unlock();
}
}
}
四、Condition使用注意事项
1. **锁的获取顺序**:
必须先获取`Lock`再操作`Condition`,否则会抛出`IllegalMonitorStateException`。错误示例:
Condition cond = lock.newCondition();
cond.await(); // 错误!未获取锁
2. **虚假唤醒问题**:
必须使用`while`循环检查条件,而非`if`语句:
// 错误写法
if (queue.isEmpty()) {
cond.await();
}
// 正确写法
while (queue.isEmpty()) {
cond.await();
}
3. **中断处理**:
优先使用`awaitInterruptibly()`处理中断:
try {
condition.awaitInterruptibly();
} catch (InterruptedException e) {
// 处理中断
Thread.currentThread().interrupt();
}
4. **性能考量**:
- 避免频繁创建`Condition`对象
- 合理选择`signal()`和`signalAll()`
- 在简单场景下`synchronized`可能更高效
五、Condition与Synchronized对比
特性 | synchronized | Condition |
---|---|---|
锁对象 | 任意对象 | 必须通过Lock创建 |
条件队列 | 单个 | 多个 |
公平性 | 不可控 | 可配置 |
超时等待 | 有限支持 | 完整支持 |
中断响应 | 不支持 | 完整支持 |
六、高级应用技巧
1. **多条件变量协作**:
实现优先级队列的线程调度:
class PriorityQueue {
private final Lock lock = new ReentrantLock();
private final Condition highPriority = lock.newCondition();
private final Condition lowPriority = lock.newCondition();
private int highCount = 0;
public void addHighPriority(Runnable task) {
lock.lock();
try {
highCount++;
highPriority.signalAll();
} finally {
lock.unlock();
}
}
public Runnable takeHighPriority() throws InterruptedException {
lock.lock();
try {
while (highCount == 0) {
highPriority.await();
}
highCount--;
return () -> System.out.println("High priority task");
} finally {
lock.unlock();
}
}
}
2. **条件传播**:
实现链式条件唤醒:
class ConditionChain {
private final Lock lock = new ReentrantLock();
private final Condition first = lock.newCondition();
private final Condition second = lock.newCondition();
private boolean state = false;
public void step1() throws InterruptedException {
lock.lock();
try {
while (!state) {
first.await();
}
System.out.println("Step 1 completed");
second.signal();
} finally {
lock.unlock();
}
}
public void trigger() {
lock.lock();
try {
state = true;
first.signalAll();
} finally {
lock.unlock();
}
}
}
3. **定时条件**:
实现带超时的资源获取:
class TimeoutResource {
private final Lock lock = new ReentrantLock();
private final Condition available = lock.newCondition();
private boolean isAvailable = false;
public String acquireWithTimeout(long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
lock.lock();
try {
long remaining = unit.toNanos(timeout);
while (!isAvailable) {
if (remaining
七、常见问题解决方案
1. **死锁问题**:
典型场景:
// 错误示例:可能导致死锁
public void methodA() {
lockA.lock();
try {
lockB.lock();
try {
// 操作
} finally {
lockB.unlock();
}
} finally {
lockA.unlock();
}
}
public void methodB() {
lockB.lock();
try {
lockA.lock();
try {
// 操作
} finally {
lockA.unlock();
}
} finally {
lockB.unlock();
}
}
解决方案:按固定顺序获取锁
2. **条件判断遗漏**:
错误示例:
// 错误:可能虚假唤醒
if (queue.size() = MAX_SIZE) {
// 等待
}
// 处理后
if (等待线程存在) {
notFull.signal();
}
3. **性能瓶颈**:
优化策略:
- 减少锁持有时间
- 合理划分临界区
- 使用读写锁分离读操作
八、最佳实践总结
1. **设计模式选择**:
- 简单场景:优先使用`synchronized`
- 复杂条件:使用`Condition`
- 需要中断:必须使用`Condition`
2. **代码结构规范**:
// 标准模板
public void method() {
lock.lock();
try {
while (条件不满足) {
condition.await();
}
// 临界区操作
// 修改共享变量
condition.signalAll(); // 或 signal()
} catch (InterruptedException e) {
// 处理中断
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
3. **监控与调试**:
- 添加日志记录锁获取情况
- 使用JConsole监控线程状态
- 设置合理的超时时间
4. **测试策略**:
- 单元测试覆盖中断场景
- 压力测试验证并发性能
- 边界条件测试(空队列、满队列等)
关键词:Java多线程、Condition接口、线程通信、Lock机制、生产者消费者模型、线程同步、虚假唤醒、中断处理、多条件队列
简介:本文详细解析了Java中Condition接口的实现原理和使用方法,通过对比synchronized机制阐述其优势,结合生产者-消费者模型、线程池调度等经典场景展示具体应用,深入探讨了虚假唤醒、中断处理等关键问题的解决方案,最后总结了最佳实践和性能优化策略,为开发者提供完整的Condition使用指南。