等待唤醒机制(Wait-Notify)是一种线程间协作的机制,用于实现线程之间的通信和同步。它主要涉及两个操作:等待(wait)和唤醒(notify)。
等待唤醒机制通常应用于多线程环境下,其中一个线程等待某个条件得到满足,而另一个线程负责在满足条件时通知等待的线程继续执行。
具体来说,当一个线程调用对象的wait()方法时,它会释放当前持有的锁,并且进入等待状态,直到其他线程通过notify()或notifyAll()方法来唤醒它。被唤醒的线程将重新尝试获取锁,然后继续执行。
而唤醒线程通常在满足某个条件时,通过调用对象的notify()或notifyAll()方法来通知正在等待的线程继续执行。notify()方法会随机选择一个等待线程进行唤醒,而notifyAll()方法会唤醒所有等待的线程,使它们进入就绪状态,等待获取锁以继续执行。
等待唤醒机制提供了一种有效的线程间协作方式,使得线程可以根据特定条件进行等待和唤醒,从而实现对共享资源的更好利用和管理。
需要注意的是,等待唤醒机制必须在同步代码块中使用,即对共享对象进行操作,以确保正确的并发控制和线程间的协作。在调用wait()、notify()或notifyAll()方法之前,线程必须先获得相应对象的锁,否则会抛出IllegalMonitorStateException异常。
下面是一个简单的Java代码示例,演示了等待唤醒机制的实现:
public class WaitNotifyExample {
public static void main(String[] args) {
Message message = new Message();
Thread producerThread = new Thread(new Producer(message));
Thread consumerThread = new Thread(new Consumer(message));
producerThread.start();
consumerThread.start();
}
}
class Message {
private String content;
private boolean isProduced;
public synchronized void produce(String content) {
while (isProduced) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.content = content;
System.out.println("Produced: " + content);
isProduced = true;
notify();
}
public synchronized String consume() {
while (!isProduced) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String consumedContent = content;
System.out.println("Consumed: " + consumedContent);
isProduced = false;
notify();
return consumedContent;
}
}
class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
@Override
public void run() {
String[] contents = {"Message 1", "Message 2", "Message 3"};
for (String content : contents) {
message.produce(content);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
String consumedContent = message.consume();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
下面我们逐步解释代码的细节:
-
首先,我们创建了一个
Message
类,用于消息传递。该类包含了一个消息内容content
和一个表示是否已经生产消息的标志isProduced
。 -
Message
类中的produce()
方法用于生产消息。在该方法中,使用synchronized
关键字确保多个线程在访问produce()
方法时的互斥性。 -
在
produce()
方法中,使用while
循环来检查是否已经生产消息。如果isProduced
为true
,说明已经生产了消息,则调用wait()
方法让当前线程进入等待状态,直到被其他线程调用notify()
方法唤醒。 -
当前线程被唤醒后,会继续执行后续代码,将传入的消息内容赋值给
content
变量,并将isProduced
标志设置为true
,表示消息已经生产。然后通过notify()
方法唤醒在等待的其他线程。 -
Message
类中的consume()
方法用于消费消息。同样,使用synchronized
关键字确保多个线程在访问consume()
方法时的互斥性。 -
在
consume()
方法中,同样使用while
循环来检查是否已经生产消息。如果isProduced
为false
,说明还没有生产消息,则调用wait()
方法让当前线程进入等待状态,直到被其他线程调用notify()
方法唤醒。 -
当前线程被唤醒后,会继续执行后续代码,将
content
变量的值作为消费的消息内容,并将isProduced
标志设置为false
,表示消息已经被消费。然后通过notify()
方法唤醒在等待的其他线程。 -
在主程序中,我们创建了一个
Message
对象和两个线程:生产者线程Producer
和消费者线程Consumer
。 -
生产者线程通过调用
message.produce(content)
方法来生产消息。在示例中,我们定义了3个消息内容,分别进行生产,并且每次生产完后等待1秒钟。 -
消费者线程通过调用
message.consume()
方法来消费消息。在示例中,我们简单地循环3次消费消息,并且每次消费后等待1秒钟。
通过以上操作,生产者和消费者线程之间进行消息的生产和消费,并且使用了等待唤醒机制进行线程间的通信和同步。当消息已经生产时,生产者线程进入等待状态,等待消费者线程消费完毕后唤醒;当消息还没有被生产时,消费者线程进入等待状态,等待生产者线程生产完毕后唤醒。
需要注意的是,为了保证正确的并发控制和线程间的协作,必须在调用wait()
、notify()
或notifyAll()
方法之前先获取锁(即使用synchronized
关键字修饰的方法或代码块),否则会抛出IllegalMonitorStateException
异常。