Condition精确唤醒等待线程

wen java案例 3

本文目录导读:

  1. 核心概念
  2. Java中的实现:Lock + Condition
  3. 典型应用场景
  4. 高级技巧与注意事项
  5. 性能对比:Condition vs synchronized
  6. 实战:异步任务协调

在并发编程中,Condition(条件变量)是实现精确唤醒等待线程的核心机制,它解决了 wait/notify 无法指定唤醒特定线程的缺陷。


核心概念

Condition等待/通知机制解耦:

  • 每个 Condition 对象维护一个独立的等待队列
  • 线程可以等待特定条件(condition.await())
  • 其他线程可以唤醒特定条件上的线程(condition.signal())

Java中的实现:Lock + Condition

基本用法

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public 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 putIndex, takIndex, count;
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            // 当缓冲区满时,等待"不满"条件
            while (count == items.length) {
                notFull.await();
            }
            items[putIndex] = x;
            if (++putIndex == items.length) putIndex = 0;
            count++;
            // 唤醒等待"不空"条件的线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            // 当缓冲区空时,等待"不空"条件
            while (count == 0) {
                notEmpty.await();
            }
            Object x = items[takIndex];
            if (++takIndex == items.length) takIndex = 0;
            count--;
            // 唤醒等待"不满"条件的线程
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

精确唤醒的工作原理

Condition精确唤醒等待线程

  • notFull.await():当前线程释放锁并进入 notFull 的等待队列
  • notFull.signal():唤醒 notFull 队列中的一个线程(通常是等待最久的)
  • notFull.signalAll():唤醒 notFull 队列中的所有线程

典型应用场景

场景1:生产者-消费者模式

public class ProducerConsumerExample {
    private final Lock lock = new ReentrantLock();
    private final Condition produced = lock.newCondition();
    private final Condition consumed = lock.newCondition();
    private int product = 0;
    private boolean available = false;
    public void produce() throws InterruptedException {
        lock.lock();
        try {
            while (available) {
                consumed.await();  // 等待产品被消费
            }
            product++;
            System.out.println("生产者生产,当前产品数:" + product);
            available = true;
            produced.signal();    // 唤醒等待产品的消费者
        } finally {
            lock.unlock();
        }
    }
    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (!available) {
                produced.await();  // 等待产品被生产
            }
            System.out.println("消费者消费,当前产品数:" + product);
            available = false;
            consumed.signal();    // 唤醒等待的生产者
        } finally {
            lock.unlock();
        }
    }
}

优势:生产者只唤醒消费者,消费者只唤醒生产者,避免了无意义的线程唤醒。


场景2:多条件精确控制(打印机队列)

class PrinterManager {
    private final Lock lock = new ReentrantLock();
    private final Condition colorAvailable = lock.newCondition();
    private final Condition blackWhiteAvailable = lock.newCondition();
    private int colorPrinters = 3;
    private int bwPrinters = 5;
    public void useColorPrinter(String task) throws InterruptedException {
        lock.lock();
        try {
            while (colorPrinters == 0) {
                colorAvailable.await();  // 等待彩色打印机
            }
            colorPrinters--;
            System.out.println(task + " 使用彩色打印机,剩余:" + colorPrinters);
            // 打印任务...
        } finally {
            lock.unlock();
        }
    }
    public void releaseColorPrinter() {
        lock.lock();
        try {
            colorPrinters++;
            colorAvailable.signal();  // 只唤醒等待彩色打印机的线程
        } finally {
            lock.unlock();
        }
    }
    // 黑白打印机方法类似...
}

高级技巧与注意事项

防止条件丢失

// 错误示例
if (!condition) {
    condition.await();  // 可能被虚假唤醒
}
// 正确做法:始终在循环中等待
while (!condition) {
    condition.await();
}

使用多个Condition实现优先级

class PriorityQueue<T> {
    private final Lock lock = new ReentrantLock();
    private final Condition highPriority = lock.newCondition();
    private final Condition lowPriority = lock.newCondition();
    private Queue<T> highQueue = new LinkedList<>();
    private Queue<T> lowQueue = new LinkedList<>();
    public void highPriorityPut(T item) {
        lock.lock();
        try {
            highQueue.add(item);
            highPriority.signal();  // 优先唤醒高优先级消费者
        } finally {
            lock.unlock();
        }
    }
    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (highQueue.isEmpty() && lowQueue.isEmpty()) {
                highPriority.await();  // 等待高优先级
                lowPriority.await();   // 等待低优先级
            }
            if (!highQueue.isEmpty()) {
                return highQueue.poll();
            }
            return lowQueue.poll();
        } finally {
            lock.unlock();
        }
    }
}

线程安全地传递条件

class ComplexConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    // 使用 volatile 保证可见性
    private volatile boolean ready = false;
    private volatile String data;
    public void producer(String data) {
        lock.lock();
        try {
            this.data = data;
            ready = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
    public String consumer(long timeout, TimeUnit unit) 
            throws InterruptedException, TimeoutException {
        lock.lock();
        try {
            while (!ready) {
                if (!condition.await(timeout, unit)) {
                    throw new TimeoutException("等待超时");
                }
            }
            return data;
        } finally {
            lock.unlock();
        }
    }
}

性能对比:Condition vs synchronized

特性 synchronized + wait/notify Lock + Condition
等待队列数 1个 多个(可精确控制)
唤醒精度 无法区分条件 可唤醒指定条件
超时控制 wait(timeout) await(timeout, unit)
可中断性 支持 支持
公平性 不可控 可配置公平锁
检查等待队列 不支持 hasWaiters(Condition)

选择建议

  • 简单场景(1个条件)→ synchronized 更简洁
  • 复杂场景(多条件/精确控制)→ Lock + Condition

实战:异步任务协调

class AsyncTaskCoordinator {
    private final Lock lock = new ReentrantLock();
    private final Condition taskCompleted = lock.newCondition();
    private volatile int completedCount = 0;
    private final int totalTasks;
    public AsyncTaskCoordinator(int totalTasks) {
        this.totalTasks = totalTasks;
    }
    public void waitForAll() throws InterruptedException {
        lock.lock();
        try {
            while (completedCount < totalTasks) {
                taskCompleted.await();
            }
            System.out.println("所有任务完成");
        } finally {
            lock.unlock();
        }
    }
    public void onTaskCompleted() {
        lock.lock();
        try {
            completedCount++;
            taskCompleted.signalAll();  // 唤醒主线程
        } finally {
            lock.unlock();
        }
    }
    // 测试用例
    public static void main(String[] args) throws InterruptedException {
        AsyncTaskCoordinator coordinator = new AsyncTaskCoordinator(3);
        // 启动3个异步任务
        for (int i = 0; i < 3; i++) {
            int taskId = i;
            new Thread(() -> {
                try {
                    Thread.sleep((long)(Math.random() * 1000));
                    System.out.println("任务 " + taskId + " 完成");
                    coordinator.onTaskCompleted();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
        coordinator.waitForAll();
        System.out.println("主线程继续执行");
    }
}

Condition 是实现精确唤醒的黄金标准:

  1. 隔离等待集:每个条件独立的等待队列
  2. 精确通知:唤醒特定条件的线程
  3. 提高效率:减少不必要的上下文切换
  4. 降低复杂度:避免复杂的条件判断

记住核心原则:把相关的线程组织到同一个Condition上,用不同的Condition隔离不同的等待原因

抱歉,评论功能暂时关闭!