我们知道,java里的线程是协作式的,在完成一项任务的过程中,因业务逻辑的需要,导致我们可能需要控制某些线程阻塞,等待另一些线程完成某部分事情后,再让线程继续执行。这个阻塞-唤醒的过程中就涉及到了线程之间的通信,也就是我接下来想说的多线程协作之等待-通知机制。java的Object类为我们提供一种实现等待-通知机制的方法,接下来了解一下wait/notify/notifyAll方法。
wait()、wait(long timeout)、wait(long timeout, int nanos)
打开Object类我们可以发现wait()和wait(long timeout, int nanos)实际上最终都会调用native方法wait(long timeout)。
wait()方法
wait(long timeout, int nanos)方法
wait(long timeout, int nanos)
重点看看native的wait(long timeout)方法。
从方法注释中我们至少可以得到三个结论:
- wait方法导致当前线程阻塞直到有线程调用了该对象的notify/notifyAl方法或者线程中断;
- 调用wait方法的线程必须持有对象的锁,否则抛出IllegalMonitorStateException;wait方法会释放锁,被唤醒后需重新竞争锁;
- wait方法的使用方式模板如下
synchronized (obj){ while(condition does not hold){ obj.wait(); } // 业务逻辑}
notify()、notifyAll()
notify方法
可以从方法注释中得到3点结论:
- notify唤醒的是任意一个在对象上等待的线程;
- notify方法不释放锁,必须等当前线程执行完成释放锁后,wait的线程才能去竞争锁来执行;
- 使用方式,需要在synchronized方法、synchronized静态方法、synchronized代码块中调用;
notifyAll方法,与notify类似,只不过notifyAll是唤醒在对象上等待的所有线程。
所以在使用过程中推荐尽量使用notifyAll,因为我们无法保证被唤醒的这个线程一定就是我们想要唤醒的线程。
基础用法,简单实现一个生产者消费者demo
public class ProducerAndConsumerDemo { static final int MAX_SIZE = 20; private static class ProduceThread implements Runnable { private final List produce; public ProduceThread(List produce) { this.produce = produce; } @Override public void run() { while (true) { synchronized (produce) { while (produce.size() == MAX_SIZE) { try { produce.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (produce.size() < MAX_SIZE) { int value = MAX_SIZE - produce.size(); produce.add(value); System.out.println(Thread.currentThread().getName() + "生产=" + value); produce.notifyAll(); } } } } } private static class ConsumeThread implements Runnable { private final List consume; public ConsumeThread(List consume) { this.consume = consume; } @Override public void run() { while (true) { synchronized (consume) { while (consume.isEmpty()) { try { consume.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (!consume.isEmpty()) { Integer remove = consume.remove(0); System.out.println(Thread.currentThread().getName() + "消费=" + remove); consume.notifyAll(); } } } } } public static void main(String[] args) { final List list = new ArrayList<>(); for (int i = 0; i < 1; i++) { new Thread(new ProduceThread(list)).start(); new Thread(new ConsumeThread(list)).start(); } }}
若有失误之处,望请指正,感谢!