本博客系列是学习并发编程过程当中的记录总结。因为文章比较多,写的时间也比较散,因此我整理了个目录贴(传送门),方便查阅。html
并发编程系列博客传送门java
当一个线程调用一个共享变量的wait()方法时,该调用线程会被阻塞挂起(进入waiting状态),直到发生下面几件事情之一才能返回:编程
另外须要注意的是,若是调用wait()方法的线程没有事先获取该对象的监视器锁,则调用wait()方法时调用线程会抛出IllegalMonitorStateException异常。若是当前线程已经获取了锁资源,调用wait方法以后会释放这个锁资源,可是只会释放当前共享变量上的锁,若是当前线程还持有其余共享变量的锁,则这些锁是不会被释放的。多线程
wait方法还有一个重载方法wait(long time),这个方法会等待time时间,若是在这个时间内没有其余线程来唤醒它的话,这个线程会本身唤醒继续得到执行机会。并发
notify方法会唤醒等待对象监视器的单个线程,若是等待对象监视器的有多个线程,则选取其中一个线程进行唤醒,到底选择唤醒哪一个线程是任意的,由CPU本身决定。若是没有再调用notify方法,其余阻塞的线程可能就永远得不到再执行的机会了。app
此外,被唤醒的线程不能立刻从wait方法返回并继续执行,它必须在获取了共享对象的监视器锁后才能够返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不必定会获取到共享对象的监视器锁,这是由于该线程还须要和其余线程一块儿竞争该锁,只有该线程竞争到了共享变量的监视器锁后才能够继续执行。框架
一个还须要注意的地方是,在共享变量上调用notifyAll()方法只会唤醒调用这个方法前调用了wait系列函数而被放入共享变量等待集合里面的线程。若是调用notifyAll()方法后一个线程调用了该共享变量的wait()方法而被放入阻塞集合,则该线程是不会被唤醒的ide
相似wait系列方法,只有当前线程获取到了共享变量的监视器锁后,才能够调用共享变量的notify()方法,不然会抛出IllegalMonitorStateException异常。函数
notify方法还有个兄弟方法notifyAll,这个方法会唤醒全部等待监视器对象的线程。学习
wait-notify模式的一个典型应用就是能够实现生产者-消费者模式。让我印象很深是我毕业那年阿里巴巴校园招聘的一个笔试题:
有一个苹果箱,有10我的向这个箱子中每次随机放入一个苹果,有10我的每次随机从这个箱子中随机拿走一个苹果,同时须要知足箱子中的苹果总数不能超过50个。请用代码实现上面的场景(不能使用并发集合框架)
如今看来,这道题不就是为wait-notify模式量身打造的一道题目么。当时水平有限,又急急忙忙的,因此记得当时写的不太好。这边从新整理下这个代码
public class AppleBox { private int appleCount; public synchronized void putApple() { while (appleCount >= 50) { try { //会释放锁 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } appleCount++; String name = Thread.currentThread().getName(); System.out.println("[" + name + "]放入一个,当前盒子中苹果数:" + appleCount); this.notifyAll(); } public synchronized void takeApple() { while (appleCount <= 0) { try { //会释放锁 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } appleCount--; String name = Thread.currentThread().getName(); System.out.println("[" + name + "]拿走一个,当前盒子中苹果数:" + appleCount); this.notifyAll(); } private static class AppleTaker implements Runnable { private AppleBox appleBox; public AppleTaker(AppleBox appleBox) { this.appleBox = appleBox; } @Override public void run() { while (true) { appleBox.takeApple(); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } private static class ApplePutter implements Runnable { private AppleBox appleBox; public ApplePutter(AppleBox appleBox) { this.appleBox = appleBox; } @Override public void run() { while (true) { appleBox.putApple(); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { AppleBox appleBox = new AppleBox(); for (int i = 0; i < 20; i++) { Thread t = new Thread(new ApplePutter(appleBox)); t.setName("ApplePutter:" + i); t.start(); } for (int i = 0; i < 20; i++) { Thread t = new Thread(new AppleTaker(appleBox)); t.setName("AppleTaker:" + i); t.start(); } } }
执行结果以下:
[ApplePutter:0]放入一个,当前盒子中苹果数:1 [ApplePutter:1]放入一个,当前盒子中苹果数:2 [ApplePutter:5]放入一个,当前盒子中苹果数:3 [ApplePutter:9]放入一个,当前盒子中苹果数:4 [ApplePutter:13]放入一个,当前盒子中苹果数:5 [ApplePutter:2]放入一个,当前盒子中苹果数:6 [ApplePutter:6]放入一个,当前盒子中苹果数:7 [ApplePutter:10]放入一个,当前盒子中苹果数:8 [ApplePutter:17]放入一个,当前盒子中苹果数:9 [ApplePutter:14]放入一个,当前盒子中苹果数:10 [ApplePutter:18]放入一个,当前盒子中苹果数:11 [ApplePutter:3]放入一个,当前盒子中苹果数:12 [ApplePutter:7]放入一个,当前盒子中苹果数:13 [ApplePutter:11]放入一个,当前盒子中苹果数:14 [ApplePutter:8]放入一个,当前盒子中苹果数:15 [ApplePutter:15]放入一个,当前盒子中苹果数:16 [ApplePutter:19]放入一个,当前盒子中苹果数:17 [ApplePutter:4]放入一个,当前盒子中苹果数:18 [AppleTaker:3]拿走一个,当前盒子中苹果数:17 [ApplePutter:12]放入一个,当前盒子中苹果数:18 [AppleTaker:1]拿走一个,当前盒子中苹果数:17 [AppleTaker:5]拿走一个,当前盒子中苹果数:16 [ApplePutter:16]放入一个,当前盒子中苹果数:17 [AppleTaker:0]拿走一个,当前盒子中苹果数:16 [AppleTaker:12]拿走一个,当前盒子中苹果数:15 [AppleTaker:8]拿走一个,当前盒子中苹果数:14 [AppleTaker:16]拿走一个,当前盒子中苹果数:13 [AppleTaker:7]拿走一个,当前盒子中苹果数:12 [AppleTaker:11]拿走一个,当前盒子中苹果数:11 [AppleTaker:19]拿走一个,当前盒子中苹果数:10 [AppleTaker:9]拿走一个,当前盒子中苹果数:9 [AppleTaker:13]拿走一个,当前盒子中苹果数:8 [AppleTaker:2]拿走一个,当前盒子中苹果数:7 [AppleTaker:6]拿走一个,当前盒子中苹果数:6 [AppleTaker:10]拿走一个,当前盒子中苹果数:5 [AppleTaker:14]拿走一个,当前盒子中苹果数:4 [AppleTaker:4]拿走一个,当前盒子中苹果数:3 [AppleTaker:15]拿走一个,当前盒子中苹果数:2 [AppleTaker:18]拿走一个,当前盒子中苹果数:1 [AppleTaker:17]拿走一个,当前盒子中苹果数:0 [ApplePutter:0]放入一个,当前盒子中苹果数:1 [ApplePutter:1]放入一个,当前盒子中苹果数:2 [ApplePutter:5]放入一个,当前盒子中苹果数:3 [ApplePutter:9]放入一个,当前盒子中苹果数:4 [ApplePutter:13]放入一个,当前盒子中苹果数:5 [ApplePutter:17]放入一个,当前盒子中苹果数:6 [ApplePutter:2]放入一个,当前盒子中苹果数:7 [ApplePutter:6]放入一个,当前盒子中苹果数:8 [ApplePutter:10]放入一个,当前盒子中苹果数:9 [ApplePutter:14]放入一个,当前盒子中苹果数:10 [ApplePutter:18]放入一个,当前盒子中苹果数:11 [ApplePutter:3]放入一个,当前盒子中苹果数:12 [ApplePutter:7]放入一个,当前盒子中苹果数:13 [ApplePutter:11]放入一个,当前盒子中苹果数:14 [ApplePutter:15]放入一个,当前盒子中苹果数:15 [ApplePutter:19]放入一个,当前盒子中苹果数:16 [AppleTaker:3]拿走一个,当前盒子中苹果数:15 [ApplePutter:4]放入一个,当前盒子中苹果数:16 [ApplePutter:8]放入一个,当前盒子中苹果数:17 [ApplePutter:12]放入一个,当前盒子中苹果数:18
**PS: 多线程编程中,最要的重要的两点是先抽象出共享变量是什么,任务类(Runner)是什么 **
生产者和消费者的逻辑均可以统一抽象成如下几个步骤:
伪代码以下:
synchronized(对象) { //这边进行循环判断的缘由是为了防止伪唤醒,也就是否是消费线程或者生产线程调用notify方法将waiting线程唤醒的 while(条件){ 对象.wait(); } //进行生产或者消费活动 doSomething(); 对象.notifyAll(); }
原文出处:https://www.cnblogs.com/54chensongxia/p/11995981.html