java多线程编程之wait、notify
所有的类都祖先类都是Object,所以所有的类都包含wait方法和notify方法。所有的类对应对象都可以作为监视器。
1. wait()方法
使当前线程等待,直到另一个线程调用此对象的notify方法或notifyAll方法。换句话说,这个方法的行为就像它只是执行调用wait(0)一样。
当前线程必须拥有此对象的监视器。线程释放此监视器的所有权,并等待,直到另一个线程通过调用notify方法或notifyAll方法通知在该对象监视器上等待的线程唤醒为止。然后线程重新获得监视器的所有权并恢复执行。
与单参数版本一样,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:
synchronized (obj) {
while (当条件不满足时)
obj.wait();
... // 满足条件时应该执行的逻辑
}
此方法只能由作为此对象监视器所有者的线程调用。有关线程成为监视器所有者的方式的描述,请参阅notify方法。
2. wait(long timeout)方法
使当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法,或者经过指定的时间。
此方法导致当前线程(称为T)将自己放置在此对象的等待集中,然后放弃对此对象的任何和所有同步声明。线程T出于线程调度目的而被禁用,并处于休眠状态,直到发生以下四种情况之一:
- 其他一些线程调用该对象的notify方法,而线程T恰好被任意选择为要唤醒的线程。
- 其他一些线程调用该对象的notifyAll方法。
- 一些其他线程中断线程T。
- 或多或少已经过了指定的实时量。然而,如果超时为零,则不考虑实时性,线程只是等待通知。
然后,线程T从该对象的等待集中移除,并重新启用线程调度。然后,它以通常的方式与其他线程竞争在对象上同步的权利;一旦它获得了对对象的控制,它对对象的所有同步声明都将恢复到原来的状态,也就是说,恢复到调用wait方法时的状态。线程T然后从wait方法的调用中返回。因此,从等待方法返回时,对象和线程T的同步状态与调用等待方法时的状态完全相同。
线程也可以在没有被通知、中断或超时的情况下唤醒,这就是所谓的虚假唤醒。虽然这种情况在实践中很少发生,但应用程序必须通过测试本应导致线程被唤醒的条件来防止这种情况,如果条件不满足,则继续等待。换句话说,等待应该总是发生在循环中,就像下面这样:
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
2.1 参数说明
timeout:等待的最长时间(以毫秒为单位)。
3. notify方法
唤醒在该对象的监视器上等待的单个线程。如果有多个线程正在等待此对象,则会选择其中一个线程进行唤醒。这种选择是任意的,由执行人员自行决定。线程通过调用对象的wait方法来等待对象的监视器。
在当前线程放弃对此对象的锁定之前,唤醒的线程将无法继续。唤醒的线程将以通常的方式与可能在该对象上主动竞争同步的多个其他线程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程时没有可靠的特权或缺点。
4. notifyAll方法
唤醒在该对象的监视器上等待的所有线程。
在当前线程放弃对此对象的锁定之前,唤醒的线程将无法继续。唤醒的线程将以通常的方式与可能在该对象上主动竞争同步的其他多个线程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程时没有可靠的特权或劣势。
此方法只能由作为此对象监视器所有者的线程调用。有关线程成为监视器所有者的方式的描述,请参阅notify方法。
5. 示例
public static void main(String[] args) throws Exception {
Object lock = new Object();
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
for (int i = 0 ;i < 5; i++) {
final int m = i;
new Thread(new Producer(String.format("生产者%s", m), lock, queue)).start();
}
new Thread(new Consumer("消费者0", lock, queue)).start();
}
private static class Producer implements Runnable {
private String name;
private Object lock;
private LinkedBlockingQueue queue;
Random random = new Random();
public Producer(String name, Object lock, LinkedBlockingQueue queue) {
this.name = name;
this.lock = lock;
this.queue = queue;
}
@Override
public void run() {
log.info("{}: 生产者开始运行", name);
synchronized (lock) {
while (true) {
Integer num = random.nextInt(100);
log.info("{} 生成了数据: {}", name, num);
queue.offer(num);
try {
Thread.sleep(1000);
lock.wait();
} catch (Exception e) {
}
}
}
}
}
private static class Consumer implements Runnable {
private String name;
private Object lock;
private LinkedBlockingQueue<Integer> queue;
public Consumer(String name, Object lock, LinkedBlockingQueue<Integer> queue) {
this.name = name;
this.lock = lock;
this.queue = queue;
}
@Override
public void run() {
log.info("{}: 消费者开始运行", name);
try {
while (true) {
synchronized (lock) {
Integer num;
while((num = queue.poll()) != null) {
log.info("消费者:{}, 消费了数据:{}", name, num);
}
lock.notifyAll();
}
Thread.sleep(10000);
}
}catch (Exception e) {
e.printStackTrace();
}
log.info("");
}
}
控制台打印如下所示
image.png