Java 等待和通知
原文:https://www.studytonight.com/java-examples/java-wait-and-notify
多线程是同时运行多个线程的过程。多线程提高了我们代码的效率,因为多个线程将问题分开,同时处理较小的部分。然而,多个线程可能需要访问一个公共资源。
在这个公共资源上使用锁来避免并行修改。当线程没有同步到使用这个锁时,就会出现不一致。在 Java 中wait()
和notify()
方法用于线程通信和同步。
让我们更多地了解这些方法。
Java 等待()方法
Java 中的Object
类有一个最终的wait()
方法。它用于暂停线程的执行。它还会使线程放弃它的锁,以便其他线程可以访问临界区。代码中修改共享资源的部分称为临界区。
此方法有三个重载签名。
- 等待()
- 等待(长时间超时)
- 等待(长超时,整数纳秒)
如果我们指定超时持续时间,那么线程将在超时后自动唤醒。如果没有提到超时,那么线程必须等待其他线程对其调用 notify。
使用wait()
方法进行同步的一般语法如下所示。
synchronized(object)
{
while(condition is false)
{
object.wait();
}
//do the task
}
Java notify()方法
notify()
方法也属于 Object 类。此方法用于唤醒等待的线程。如果多个线程正在等待,那么随机选择的线程将被唤醒。
请注意,此方法不会使调用(或通知)线程放弃其锁。
Java notifyAll()方法
notifyAll()方法与 notify()方法非常相似,但是它将唤醒所有等待的线程,而不是只唤醒一个线程。
为什么 wait()被包含在 while 循环中?
wait()
方法应该包含在 while 循环中,因为线程可以在没有notify()
调用的情况下被唤醒。这被称为虚假唤醒。while 循环的另一个原因是一个邪恶的线程可能会调用 notifyAll()并唤醒所有等待的线程。
线程需要检查条件,如果条件不满足,那么它们应该继续等待。
例子:生产者和消费者问题
让我们通过一个例子来了解如何使用 wait()和 notify()。我们将用这些方法解决传统的生产者-消费者问题。我们先来了解生产者-消费者问题。
- 这个问题涉及到生产者和消费者。
- 生产者将生产物品并将它们添加到缓冲区。
- 消费者将消费来自缓冲区的产品。
- 如果缓冲区已满,生产者不应生产新项目。
- 如果缓冲区为空,使用者不应使用。
让我们为生产者类编写代码。这个类将实现可运行的接口。它将有一个名为缓冲区的成员。
当缓冲区已满时,生产者应调用wait()
方法。缓冲区的最大容量设置为 5 个元素。在生成一个新项目并将其添加到缓冲区后,它应该通知使用者线程。如果消费线程正在等待,那么它将醒来并开始消费。
class Producer implements Runnable
{
//buffer to store the produced items
private final LinkedList<Integer> buffer;
Producer(LinkedList<Integer> buffer)
{
this.buffer = buffer;
}
@Override
public void run()
{
//Infinitely produce items
while(true)
{
try {
this.produce();
}
catch(Exception e) {
System.out.print(e);
}
}
}
public void produce() throws InterruptedException
{
synchronized(buffer)
{
//If the buffer is full then wait
while(buffer.size() == 5)
{
System.out.println("Producer is waiting");
buffer.wait();
}
//Produce a new random number
Random r = new Random();
int num = r.nextInt(100);
System.out.println("Producer produced: " + num);
buffer.add(num);
buffer.notifyAll();
Thread.sleep(10);
}
}
}
如果缓冲区为空,消费者类应该等待。当它获取锁时,使用者应该从缓冲区弹出第一个元素并使用它。完成后,使用者应该通知生产者线程。
class Consumer implements Runnable
{
//buffer to consume items from
private final LinkedList<Integer> buffer;
Consumer(LinkedList<Integer> buffer)
{
this.buffer = buffer;
}
@Override
public void run()
{
//Infinitely consume
while(true)
{
try {
this.consume();
}
catch(Exception e) {
System.out.print(e);
}
}
}
public void consume() throws InterruptedException
{
synchronized(buffer)
{
//Wait if the buffer is empty
while(buffer.size() == 0)
{
System.out.println("Consumer is waiting");
buffer.wait();
}
//Consume the first item from the buffer
int num = buffer.remove(0);
System.out.println("Consumer consumed: " + num);
buffer.notifyAll();
Thread.sleep(5);
}
}
}
请注意,我们使用同步块来确保一次只有一个线程可以访问缓冲区。
下面显示了上述方法的演示。
public class WaitNotifyDemo
{
public static void main(String args[])
{
LinkedList<Integer> buffer = new LinkedList<>();
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
Thread p = new Thread(producer);
Thread c = new Thread(consumer);
p.start();
c.start();
}
}
生产者生产:26 生产者生产:37 消费者消费:26 消费者消费:37 消费者等待 生产者生产:66 生产者生产:91 生产者生产:51 生产者生产:88 生产者生产:61 生产者等待
摘要
wait(), notify()
和notifyAll()
方法用于线程通信和同步。
在本教程中,我们学习了如何使用这些方法。我们还学习了如何使用这些方法解决生产者-消费者问题。请注意,这些是传统的方法,使用起来比新的 API 复杂一点。