当前位置: 技术文章>> Java 中如何使用 Condition 实现等待和通知机制?

文章标题:Java 中如何使用 Condition 实现等待和通知机制?
  • 文章分类: 后端
  • 8294 阅读

在Java中,Condition 接口是 java.util.concurrent.locks 包下的一部分,它提供了一种更为灵活和强大的线程间通信机制,相较于传统的 Object 监视器方法(如 wait()notify()notifyAll())。Condition 接口与锁(通常是 ReentrantLock)一起使用,允许多个条件变量(Condition)与同一个锁关联,从而允许多个线程在不同的条件上等待和唤醒,提高了并发编程的灵活性和效率。

引入 Condition

在Java中,Condition 接口主要用于替代传统的 Object 监视器方法,以提供更精细化的控制。使用 Condition 时,你需要先获取一个锁(如 ReentrantLock),然后通过这个锁来创建 Condition 对象。每个 Condition 实例管理着那些处于等待状态的线程,因为它们依赖于某个特定的条件。

基本用法

1. 创建锁和条件变量

首先,你需要创建一个锁(例如 ReentrantLock),然后通过这个锁来创建 Condition 对象。

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

2. 等待(Waiting)

当线程需要等待某个条件时,它会先获取锁,然后调用 Condition 对象的 await() 方法。调用 await() 方法后,线程会释放锁并进入等待状态,直到其他线程调用同一个 Condition 对象的 signal()signalAll() 方法将其唤醒。

lock.lock();
try {
    // 等待条件满足
    while (!conditionMet) {
        condition.await(); // 释放锁并进入等待状态
    }
    // 条件满足后执行的操作
} finally {
    lock.unlock(); // 无论是否发生异常,最后都要释放锁
}

注意,使用 while 循环而不是 if 语句来检查条件,这是因为在等待期间条件可能由其他线程多次改变,需要确保只有在条件真正满足时才继续执行。

3. 通知(Signaling)

当条件变量的条件变为满足时,某个线程会调用 Condition 对象的 signal()signalAll() 方法来唤醒一个或所有等待的线程。

  • signal() 方法唤醒等待该条件变量的一个线程(如果有的话)。
  • signalAll() 方法唤醒等待该条件变量的所有线程。
lock.lock();
try {
    // 改变条件变量状态
    conditionMet = true;
    // 唤醒一个或多个等待的线程
    condition.signalAll(); // 示例中使用signalAll,实际使用时根据需求选择signal或signalAll
} finally {
    lock.unlock();
}

示例:生产者-消费者问题

为了更好地理解 Condition 的使用,我们可以通过一个经典的生产者-消费者问题来演示。在这个问题中,生产者线程生产产品放入缓冲区,消费者线程从缓冲区中取出产品。我们使用 ReentrantLockCondition 来控制生产和消费之间的同步。

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumerExample {

    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;
    private final Lock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await(); // 等待缓冲区不满
            }
            queue.add(value);
            System.out.println("Produced: " + value);
            notEmpty.signal(); // 唤醒一个等待的消费者
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await(); // 等待缓冲区不为空
            }
            int value = queue.poll();
            System.out.println("Consumed: " + value);
            notFull.signal(); // 唤醒一个等待的生产者
        } finally {
            lock.unlock();
        }
    }

    // 示例主函数,启动生产者和消费者线程
    public static void main(String[] args) {
        ProducerConsumerExample example = new ProducerConsumerExample();

        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    example.produce(i);
                    Thread.sleep(100); // 模拟生产耗时
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    example.consume();
                    Thread.sleep(150); // 模拟消费耗时
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        producer.start();
        consumer.start();
    }
}

在这个例子中,我们创建了两个条件变量 notEmptynotFull,分别用于表示缓冲区不为空和缓冲区不满。生产者线程在尝试向队列中添加元素时会检查队列是否已满,如果已满则等待 notFull 条件变量;消费者线程在尝试从队列中移除元素时会检查队列是否为空,如果为空则等待 notEmpty 条件变量。

总结

Condition 接口提供了一种比传统 Object 监视器方法更灵活和强大的线程间通信机制。通过与 ReentrantLock 一起使用,Condition 允许更精细地控制哪些线程应该被唤醒,以及基于哪些条件进行等待。这种机制在处理复杂的并发问题时非常有用,如生产者-消费者问题、读写锁等。

希望这个详细的解释和示例能帮助你更好地理解如何在Java中使用 Condition 来实现等待和通知机制。如果你在学习或实践中遇到更多问题,不妨访问码小课网站,那里有更多深入的技术文章和实战教程等待你的探索。

推荐文章