condition在java中确实可以看作是lock锁的增强版,它通过await()和signal()/signalall()提供更细粒度的线程同步机制。1. await()用于线程等待并释放锁;2. signal()唤醒一个等待线程,signalall()唤醒所有等待线程。与wait()/notify()相比,condition必须显式绑定到lock,支持多个等待队列,从而实现更灵活的控制。例如,在生产者-消费者模型中使用notfull和notempty两个condition分别管理缓冲区状态。使用condition时应在循环中检查条件以防止虚假唤醒或条件变化导致的错误。选择signal()还是signalall()取决于是否需要唤醒单个或多个线程,signalall()通常更安全。当需要更精细的线程控制或多等待队列时应使用condition,简单场景可继续使用wait()/notify()。
Condition在Java中可以看作是Lock锁的增强版,它允许线程在满足特定条件时挂起(等待),并在其他线程改变条件后被唤醒。这提供了一种比wait()/notify()更细粒度的线程同步机制。
解决方案
Condition接口的核心在于await()(等待)和signal()/signalAll()(通知)。await()类似于Object.wait(),它释放锁并让线程进入等待状态,直到被signal()或signalAll()唤醒。signal()唤醒一个等待的线程,signalAll()唤醒所有等待的线程。
立即学习“Java免费学习笔记(深入)”;
下面是一个简单的例子,展示了如何使用Condition实现生产者-消费者模型:
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 ProducerConsumer { private final Queue<Integer> buffer = new LinkedList<>(); private final int maxSize = 10; private final Lock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); public void produce(int value) throws InterruptedException { lock.lock(); try { while (buffer.size() == maxSize) { notFull.await(); // 缓冲区已满,等待消费者消费 } buffer.offer(value); System.out.println("Produced: " + value); notEmpty.signalAll(); // 通知消费者可以消费 } finally { lock.unlock(); } } public int consume() throws InterruptedException { lock.lock(); try { while (buffer.isEmpty()) { notEmpty.await(); // 缓冲区为空,等待生产者生产 } int value = buffer.poll(); System.out.println("Consumed: " + value); notFull.signalAll(); // 通知生产者可以生产 return value; } finally { lock.unlock(); } } public static void main(String[] args) { ProducerConsumer pc = new ProducerConsumer(); // 生产者线程 new Thread(() -> { try { for (int i = 0; i < 20; i++) { pc.produce(i); Thread.sleep((long) (Math.random() * 100)); // 模拟生产时间 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); // 消费者线程 new Thread(() -> { try { for (int i = 0; i < 20; i++) { pc.consume(); Thread.sleep((long) (Math.random() * 150)); // 模拟消费时间 } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } }
wait()/notify()是Java内置的对象监视器机制的一部分,每个对象都关联一个监视器锁。Condition则必须与Lock一起使用,提供更灵活的线程同步控制。
当需要更精细的线程控制,或者需要多个等待队列时,应该使用Condition。如果简单的同步需求,wait()/notify()可能就足够了。选择哪个取决于具体场景的复杂程度。
即使线程被signal()或signalAll()唤醒,也可能出现“虚假唤醒”(spurious wakeup),即线程在没有被通知的情况下被唤醒。此外,即使线程确实是被通知唤醒的,在它重新获得锁并继续执行之前,其他线程可能已经改变了条件。
因此,必须在循环中检查条件,确保线程在满足条件时才继续执行。在上面的例子中,while (buffer.size() == maxSize)和while (buffer.isEmpty())就是这样的循环。如果没有这些循环,可能会出现缓冲区溢出或从空缓冲区消费的情况。
signal()唤醒一个等待在Condition上的线程,具体唤醒哪个线程取决于JVM的实现。signalAll()唤醒所有等待在Condition上的线程。
选择哪个取决于具体的需求。如果只需要唤醒一个线程来处理任务,并且知道只有一个线程可以处理,那么signal()更高效。如果多个线程都可以处理任务,或者不确定哪个线程最适合处理,那么signalAll()更安全。
在生产者-消费者模型中,使用signalAll()更安全,因为生产者可能需要唤醒多个消费者(如果缓冲区中有多个元素),而消费者可能需要唤醒多个生产者(如果缓冲区未满)。即使某个线程被唤醒后发现条件不满足,它也可以重新进入等待状态。
以上就是Java中如何用Condition实现线程间通信的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号