线程间协作
多线程开发中,线程往往都不是孤立的。一个线程往往需要多线程协作完成其待执行的任务。等待唤醒机制就是用来协调线程间的协作。例如:街边的小吃店都是生产一份等销售完再生产,这是典型的生产者消费者模式。下面用代码实现这个场景。
等待唤醒机制的好处:
节省cpu。线程间通讯也可以通过轮询的方式来检查条件进行协作,但是会消耗大量cpu。用生产者/消费者模式举例。在生产者生产的时候,消费者并不需要执行。如果不用等待唤醒机制,消费者只能轮询监控生存者是否完成生产,消耗cpu。通过等待唤醒机制,可以在生产这生产时,消费者进入等待状态,不消耗cpu。待生存者生产完成后再唤醒消费者。
wait/notify
内部锁是通过wait/notify/notifyAll这三个方法实现等待唤醒。wait方法会是一个线程进入等待状态,notify会随机唤醒一个等待状态的线程,notifyAll会唤醒所有等待的线程。
1 | void notify() |
注意:
- wait/notify/notifyAll只能在内部锁作用范围内调用。
- wait/notify/notifyAll都是通过该内部锁的锁对象调用。
- wait会释放锁。
生存者消费者模式代码
1 | package Demo1; |
结果如下,生产一个产品,再消费一个产品。
1 | product............id:1 |
wait/notify 存在的问题
- 过早唤醒。因为notify唤醒具有随机性。在不确保一定能唤醒想要唤醒的线程时,必须使用notifyAll。notifyAll会将所有线程唤醒,有些线程过早被唤醒,浪费资源。例如:
如果个多生产者多消费者模式,由于notify唤醒线程的随机性,有可能唤醒的都是生产者或消费者线程。这时需要notifyAll来实现唤醒。notifyAll存在的弊端就是会把所有等待线程唤醒,无论是生产者还是消费者。这个问题可以通过显示锁的Condition来解决。
显示锁的等待唤醒机制 await/signal
jdk1.5加入的Lock可以通过创建多个Condition对象来实现分组的等待和唤醒。Condition对象只会唤醒用该Condition对象调用等待的对象。
Condition的方法
1 | void await() |
Condtion版本生存者/消费者模式
以下Product2类,其他类和上面的一致。
1 | package Demo2; |