wait和notify
Posted datartvinci
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了wait和notify相关的知识,希望对你有一定的参考价值。
-
基本用法?
-
说说wait?
-
说说notify?
-
为什么要synchronized?
1.简介
wait()和notify()是用于多线程之间协作的方法。如果一个线程调用了wait(),会阻塞直到其他线程调用了notify()。
2.wait()
如果调用了某个对象的wait(),当前线程会阻塞,直到其他线程调用这个对象的notify()方法,当前线程才有机会继续运行。
为了通俗易懂,接下来通过synchronized获取monitor简称为获取锁。
当前线程必须获取锁才能调用wait(),如果是当前类的锁,则是wait(),如果是Object的锁,则是obj.wait()。调用wait()会立刻释放锁,然后阻塞。
一共有两种方法能让当前线程从阻塞状态恢复,然后继续运行:
-
其他线程获取了这个对象的锁并调用了notify()或notifyAll(),且当前线程获取锁成功的前提下,继续执行下去。
-
其他线程调用当前线程的"中断方法"interrupt(),且当前线程获取锁成功的前提下,当前线程则会捕获"中断异常"InterruptedException,然后继续执行下去。
3.notify()和notifyAll()
3.1.notify()
获取锁后调用notify(),则随机唤醒一个阻塞的线程。被唤醒的线程并不能立刻执行,处于Runnable状态,需要获取到锁成功,甚至与其他线程竞争同一个锁成功后,才能继续运行。
与wait()不同,调用notify()之后并不会立即释放锁。
3.2.notifyAll()
与notify()工作机制一样,但是唤醒所有线程,所有调用了wait()的线程都处于Runnable状态,谁先获取锁,谁就先执行。
4.为什么要synchronized
class BlockingQueue Queue<String> buffer = new LinkedList<String>(); ? public void give(String data) buffer.add(data); notify(); // Since someone may be waiting in take! ? public String take() throws InterruptedException while (buffer.isEmpty()) // don‘t use "if" due to spurious wakeups. wait(); return buffer.remove();
1.一个消费者线程调用take(),看到队列是空的
2.在消费者调用wait()之前,生产者线程过来调用了give()方法,此时队列非空
3.消费者线程调用wait(),进入阻塞状态
4.如果不再有生产者线程调用give(),那消费者线程就永远阻塞下去
显然,用synchronized来保证 条件判断(buffer.isEmpty())和wait()这两个操作之间不会有其他线程调用notify,就能解决这个问题。
https://stackoverflow.com/questions/2779484/why-must-wait-always-be-in-synchronized-block
能够使用synchronized解决这个问题的关键在于 wait()的调用会释放锁
5.用法
编程范式
synchronized (obj) while (<condition does not hold>) obj.wait(timeout); ... // Perform action appropriate to condition synchronized (obj) if(condition) obj.notify();
<https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html
wait和notify都需要在synchronized块中调用,否则抛出IllegalMonitorStateException。
条件判断为什么用while而不是if?因为从阻塞状态恢复之后,条件可能已经变化了,需要重新判断,代码较冗余:
synchronized (monitor) // 判断条件谓词是否得到满足 if(!locked) // 等待唤醒 monitor.wait(); if(locked) // 处理其他的业务逻辑 else // 跳转到monitor.wait();
生产者消费者模型
消费者:
private String message; ? private boolean empty = true; public synchronized String take() while (empty) try wait(); catch (InterruptedException e) empty = true; notifyAll(); return message;
生产者:
public synchronized void put(String message) while (!empty) try wait(); catch (InterruptedException e) empty = false; this.message = message; notifyAll();
https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
使用wait和notify让两个线程交替打印数字
boolean isOdd = false; int i = 0; ? public synchronized void odd() while (i <= 100) while (isOdd) try wait(); catch (InterruptedException e) i++; isOdd = true; if(i>100) notify(); break; System.out.println("odd" + i); notify(); ? public synchronized void even() while (i <= 100) while (!isOdd) try wait(); catch (InterruptedException e) isOdd = false; i++; if(i>100) notify(); break; System.out.println("even" + i); notify();
拓展:n个线程交替打印
final Object obj = new Object(); int num = 1; int lastThread = 0; ? class Task implements Runnable final int index; final int n; ? Task(int i, int j) index = i; n = j; ? @Override public void run() synchronized (obj) while (num <= 100) while (lastThread % n != index - 1) try obj.wait(); catch (InterruptedException e) e.printStackTrace(); if (num > 100) return; System.out.println("thread:" + index + ",print:" + num); num++; lastThread++; obj.notifyAll(); ? @Test public void testNThread() throws InterruptedException int n = 10; for (int i = 1; i <= n; i++) Thread thread = new Thread(new Task(i, n)); thread.start(); if (i == n) thread.join();
以上是关于wait和notify的主要内容,如果未能解决你的问题,请参考以下文章
Java中 wait和await notify和signal的区别
关于wait,notify,notifyall,sleep方法的思考