Java多线程学习(七)
2016-03-07 15:16
423 查看
等待/通知模式中最经典的案例当属生产者/消费者模式,此模式有几种变形,但都是基于wait/notify的。
生产者/消费者模式有两种类型:操作值的和操作栈的。下面分别从这两方面来讲解生产者/消费者模式。
操作值
①一生产与一消费
②多生产与多消费:操作值--有可能进入假死状态
“假死”状态是因为线程连续唤醒同类造成的,生产者1唤醒生产者2,消费者1唤醒消费者2。如何解决?不仅唤醒同类,而且将异类也一起唤醒。即将生产者和消费者中的notify方法换为notifyAll方法。
操作栈
①一生产与一消费
②一生产和多消费---可能遭遇条件改变抛出的异常和假死情况
这是因为条件改变后没有得到即时的响应,解决这个问题的方法是,将if改为while语句即可;
解决方法:将notify()方法改为notifyAll()方法即可。
注明:最后一种while + notifyAll的类型同样适用于多生产者和一消费者,以及多生产者和多消费者这两种情况。
生产者/消费者模式有两种类型:操作值的和操作栈的。下面分别从这两方面来讲解生产者/消费者模式。
操作值
①一生产与一消费
public class P { private String lock; //操作值 public P(String lock){ this.lock = lock; } public void setValue(){ synchronized(lock){ if(!ValueObject.value.equals("")){ lock.wait(); } String value = System.currentTimeMillis(); ValueObject.value = value; lock.notify(); } } } public class C { private String lock; //操作值 public C(String lock){ this.lock = lock; } public void getValue(){ synchronized(lock){ if(ValueObject.value.equals("")){ lock.wait(); } ValueObject.value = ""; lock.notify(); } } } public class ValueObject { public static String value = ""; } public class ThreadP extends Thread { private P p; public ThreadP(P p){ this.p = p; } public void run(){ while(true){ p.setValue(); } } } public class ThreadC extends Thread { private C c; public ThreadC(C c){ this.c = c; } public void run(){ while(true){ c.getValue(); } } } public class Run{ public static void main(String [] args){ String lock = new String(""); P p = new P(lock); C c = new C(lock); ThreadP tp = new ThreadP(p); ThreadC tc = new ThreadC(c); tp.start(); tc.start(); } } 运行结果:不断地循环运行生产者的setValue方法和消费者的getValue方法,不停的交换数据。分析:tp线程首先运行,lock == “”,生产者p发现尚未生产,所以先生产,在下一次循环中发现value != “”;即生产出的产品尚未被消费,所以先等待进入wait状态。tc线程开始运行,发现 value!=“”,产品还没有被消费,所以先消费,在下一次循环中发现 value == “”,即生产者还没有生产,所以进入wait状态,让出控制权,让生产者继续生产...进入一种无限循环中。
②多生产与多消费:操作值--有可能进入假死状态
public class P { private String lock; //操作值 public P(String lock){ this.lock = lock; } public void setValue(){ synchronized(lock){ while(!ValueObject.value.equals("")){ lock.wait(); } String value = System.currentTimeMillis(); ValueObject.value = value; lock.notify(); } } } public class C { private String lock; //操作值 public C(String lock){ this.lock = lock; } public void getValue(){ synchronized(lock){ while(ValueObject.value.equals("")){ lock.wait(); } ValueObject.value = ""; lock.notify(); } } } public class ValueObject { public static String value = ""; } public class ThreadP extends Thread { private P p; public ThreadP(P p){ this.p = p; } public void run(){ while(true){ p.setValue(); } } } public class ThreadC extends Thread { private C c; public ThreadC(C c){ this.c = c; } public void run(){ while(true){ c.getValue(); } } } public class Run{ public static void main(String [] args){ String lock = new String(""); P p = new P(lock); C c = new C(lock); ThreadP tp1 = new ThreadP(p); ThreadP tp2 = new ThreadP(p); ThreadC tc1 = new ThreadC(c); ThreadC tc1 = new ThreadC(c); tp1.start(); tc1.start(); tp2.start(); tc2.start(); } } 运行结果:极有可能出现假死情况,即四个线程都处于wait状态。分析:首先生产者1在检查了条件,即value==“”后开始生产,然后调用notify方法,在下一次while循环检查条件是发现value != “”;进入wait状态;下一次若被生产者2抢到控制权,发现已经生产,则生产者2直接进入wait状态。然后消费者1检查发现value != “”,开始消费,在下一次while循环中,发现value == “”,即尚未生产,则进入wait状态,这时若消费者2抢到了锁, 也发现尚未生产,则也进入wait状态;生产者1又一次抢到了锁,开始生产,在下一次while循环中,发现尚未消费,进入了wait状态;此时若又由生产者2拿到了锁,它发现尚未消费,则也进入wait状态。现在,四个进程都在等待,进入了假死状态。
“假死”状态是因为线程连续唤醒同类造成的,生产者1唤醒生产者2,消费者1唤醒消费者2。如何解决?不仅唤醒同类,而且将异类也一起唤醒。即将生产者和消费者中的notify方法换为notifyAll方法。
操作栈
①一生产与一消费
public class MyStack { private List list = new ArrayList(); //操作栈
synchronized public void push(){ if(list.size() == 1){ this.wait(); } list.add("str"); this.notify(); } synchronized public void pop(){ if(list.size() == 0){ this.wait(); } list.remove(0); this.notify(); } } public class P { private MyStack myStack; public P(MyStack s){ this.myStack = s; } public void pushService(){ myStack.push(); } } public class C { private MyStack myStack; public C(MyStack s){ this.myStack = s; } public void popService(){ myStack.pop(); } } public class ThreadP extends Thread { private P p; public ThreadP(P p){ this.p = p; } public void run(){ while(true){ p.pushService(); } } } public class ThreadC extends Thread { private C c; public ThreadC(C c){ this.c = c; } public void run(){ while(true){ c.popService(); } } } public class Run{ public static void main(String [] args){ MyStack muStack= new MyStack(); P p = new P(myStack); C c = new C(myStack); ThreadP tp = new ThreadP(p); ThreadC tc = new ThreadC(c); tp.start(); tc.start(); } } 运行结果:size不会超过1,生产和消费过程在轮流执行。分析:生产者首先运行,size==0,首先生产,在下一次while循环是进入wait状态;消费者运行,首先消费,在下一次while循环时发现还未生产,进入wait状态。唤醒生产者进行生产。。。不断循环此过程。
②一生产和多消费---可能遭遇条件改变抛出的异常和假死情况
上例的代码不进行更改,只修改运行类如下: public class Run{ public static void main(String [] args){ MyStack muStack= new MyStack(); P p = new P(myStack); C c1 = new C(myStack); C c2 = new C(myStack); C c3 = new C(myStack); C c4 = new C(myStack); C c5 = new C(myStack); ThreadP tp = new ThreadP(p); ThreadC tc1 = new ThreadC(c1); ThreadC tc2 = new ThreadC(c2); ThreadC tc3 = new ThreadC(c3); ThreadC tc4 = new ThreadC(c4); ThreadC tc5 = new ThreadC(c5); tp.start(); tc.start(); } } 运行结果:在某些几率下出现异常IndexOutOfBoundException。分析:p1进行生产后进入wait状态;c1开始消费,消费后进入wait状态,假设此时消费者c2、c3、c4、c5都进入wait状态。p1有开始生产,然后唤醒c2进行消费后又进入wait状态,然后唤醒c3,由于代码中是if结构,从wait中醒来时不会再检查条件,而是直接进行remove操作,这时抛出异常。
这是因为条件改变后没有得到即时的响应,解决这个问题的方法是,将if改为while语句即可;
public class MyStack { private List list = new ArrayList(); //操作栈分析:因为是while循环,那么消费者在检查条件后,优惠进入wait状态,这是生产者和所有的消费者都进入了wait状态。
synchronized public void push(){
while(list.size() == 1){
this.wait();
}
list.add("str");
this.notify();
}
synchronized public void pop(){
while(list.size() == 0){
this.wait();
}
list.remove(0);
this.notify();
}
}
运行结果:这次不会抛出异常,但是出现假死情况。
解决方法:将notify()方法改为notifyAll()方法即可。
public class MyStack { private List list = new ArrayList(); //操作栈
synchronized public void push(){ while(list.size() == 1){ this.wait(); } list.add("str"); this.notifyAll(); } synchronized public void pop(){ while(list.size() == 0){ this.wait(); } list.remove(0); this.notifyAll(); } } 运行结果:这次不会抛出异常,页不会出现假死情况。程序会正常运行下去。
注明:最后一种while + notifyAll的类型同样适用于多生产者和一消费者,以及多生产者和多消费者这两种情况。
相关文章推荐
- Java内部类
- 属于你的第一个Java程序
- spring中H2数据库的使用
- Binary Tree Zigzag Level Order Traversal 【Java】
- 将Extjs文件拷入eclipse工程下卡死问题
- javacript use strict 严格模式作用及深析
- java 初识String
- Java 导出Excel方法三
- Java中的可变参数
- Java 导出Excel方法二
- java程序设计基础_陈国君版第五版_第三章习题
- Spring XML中<beans>中属性概述
- Struts2的类型转换及输入校验(数组类型的使用)
- Java 导出Excel方法一
- Java5实现阻塞队列
- eclipse运行Android项目出现“The connection to adb is down, and a severe error has occured. You must restart adb and Eclipse. ”
- java XML转JSON格式
- java的定时器功能
- mac os intellij如何快路查看一个java类的所有方法,结构
- java中的继承