Java多线程(3)——多线程通信(传智播客毕老师视频讲解)
来源:互联网 发布:spss问卷数据录入 编辑:程序博客网 时间:2024/06/04 23:23
多线程通信:
其实就是多个线程操作同一个资源,但操作动作不同。
1、安全问题
即由于非同步时,引起的安全问题,如下代码
public class Factory {private int num;private int value;private boolean flag=true;public void setMessage(int num,int value){try{Thread.sleep(100);}catch(Exception e){}//while(!flag)//try{wait();}catch(Exception e){}this.num=num;this.value=value;System.out.println(Thread.currentThread().getName()+"......."+"生产"+".."+this.num);flag=false;//notifyAll();}public void getMessage(){try{Thread.sleep(100);}catch(Exception e){}//while(flag)//try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"......."+"消费"+"......."+this.num);flag=true;//notifyAll();}}public class Producer implements Runnable{private Factory r;int num=0;int value=5;public Producer(Factory r){this.r=r;}public void run(){while(true){r.setMessage(num++,value);}}}public class Consumer implements Runnable{private Factory r;public Consumer(Factory r){this.r=r;}public void run(){while(true){r.getMessage();}}}public class Wakeup {public static void main(String arg[]){Factory r=new Factory();Producer r1=new Producer(r);//线程1Consumer r2=new Consumer(r);//线程2//Consumer r3=new Consumer(r);Thread t1=new Thread(r1);Thread t2=new Thread(r2);//Thread t3=new Thread(r3);t1.start();t2.start();//t3.start();}}运行结果如下:
由结果可以看出,尚未生产8号时,就已经消费了8号,这不科学,所以要引用同步来解决上述问题,即在操作的方法上将其变为同步方法,修改部分代码如下:
public class Factory {private int num;private int value;private boolean flag=true;public <span style="color:#ff0000;">synchronized</span> void setMessage(int num,int value){try{Thread.sleep(100);}catch(Exception e){}//while(!flag)//try{wait();}catch(Exception e){}this.num=num;this.value=value;System.out.println(Thread.currentThread().getName()+"......."+"生产"+".."+this.num);flag=false;//notifyAll();}public <span style="color:#ff0000;">synchronized</span> void getMessage(){try{Thread.sleep(100);}catch(Exception e){}//while(flag)//try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"......."+"消费"+"......."+this.num);flag=true;//notifyAll();}}结果如下:
这样是没问题了,可是也不能一直消费10啊,我想要生产一个消费一个
2、等待唤醒
wait(); 等待,当线程调用了wait()方法时,它会释放掉对象的锁。
notify(); 一般唤醒线程池中同一把锁的第一个等待线程
notifyAll(); 唤醒线程池中同一把锁所有线程
都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才有锁;
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程只有锁,只有同一个锁上的被等
待线程,可以被同一个锁上的notify唤醒,不可以对不同锁上的线程进行唤醒;
也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义
Object类中。
生产者消费者:
2.1
public class Factory {private int num;private int value;private boolean flag=true; //生产消费标识符public synchronized void setMessage(int num,int value){try{Thread.sleep(100);}catch(Exception e){}if(!flag) //如果flag为真,表示现在还没有生产,应进行生产;如果为假,表示现在有存货,等着消费,不进行生产try{this.wait();}catch(Exception e){}this.num=num;this.value=value;System.out.println(Thread.currentThread().getName()+"......."+"生产"+".."+this.num);flag=false;this.notify();}public synchronized void getMessage(){try{Thread.sleep(100);}catch(Exception e){}if(flag) <span style="font-family: Arial, Helvetica, sans-serif;">//如果flag为真,表示现在还没有消费,应进行消费;如果为假,表示现在无存货,等着生产,不进行消费</span>try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"......."+"消费"+"......."+this.num);flag=true;this.notify();}}运行结果如下:
可见是生产一个消费一个,具体流程如下;
开启线程t1后,主线程可能也会离开开启线程t2,如果t2掌握了cpu执行权,则进入t2的run()方法中,然后调用r.getMessage()方法,由于该方法是同步方法,如果其有执行资格,便能进入该方法中,进入后,先上锁(this锁),然后判断flag,发现是true,于是进入this.wait(),放弃执行资格;然后线程t1就进入setMessage()方法中,判断flag,发现是true,不进入this.wait(),进行设定值的操作,然后在最后将标志flag设置为假,并用this.notify()唤醒线程t2;使得线程t2从之前的this.wait()语句后开始执行,不必判断flag,进行输出打印,然后在最后将flag标志设置为true,并用this.notify()唤醒线程t1(如果线程t1被wait()的话);依次不断重复……
2.2
在上述代码的基础上,再增加一个线程t3
public class Wakeup {public static void main(String arg[]){Factory r=new Factory();Producer r1=new Producer(r);//线程1Consumer r2=new Consumer(r);//线程2Consumer r3=new Consumer(r);Thread t1=new Thread(r1);Thread t2=new Thread(r2);Thread t3=new Thread(r3);t1.start();t2.start();t3.start();}}结果运行如下:
发现一个生产有时变成了两个消费,这明显不对,其过程如下
- 假设t1获取到执行权,判断flag,为true,不需要等待,开始生产,设置flag为false,norify完后,t1可能还持有执行权,则循环回来继续执行,判断flag为false,则进入wait(),t1放弃资格;
- 此时t2、t3、t4都有可能抢到资格,假设t2抢到了,进来判断flag为false,则进入wait(),t2放弃资格;
- 此时t3、t4有可能抢到资格,假设t3抢到了,进来判断flag为false,进行消费操作,然后设置flag为true,notify完后,把t1唤醒了,假设t3还持有执行权,循环回来继续执行,判断flag为true,进入wait();
- 此时t4、t1有可能抢到资格,假设t4抢到了,进来判断flag为true,则进入wait(),t4放弃资格;
- 此时就只t1有可能抢到资格,t1进来,不用判断flag,从wait()后开始生产,然后设置flag为false,notify完后,t2被唤醒,假设t1还持有执行权,循环回来继续执行,判断flag为false,进入wait();
- 此时只有t2有资格,不用判断flag,从wait()后开始生产,然后设置flag为false,再notify。
然后我们程序就悲剧了,连续生产了两个;连续消费两个也是同理,原因就在于被唤醒后,不用判断flag,那么我们修改程序,用while代替if,让其被唤醒后依然判断flag,代码如下
public class Factory {private int num;private int value;private boolean flag=true;public synchronized void setMessage(int num,int value){try{Thread.sleep(10);}catch(Exception e){}<span style="color:#ff0000;">while</span>(!flag)try{this.wait();}catch(Exception e){}this.num=num;this.value=value;System.out.println(Thread.currentThread().getName()+"......."+"生产"+".."+this.num);flag=false;this.notify();}public synchronized void getMessage(){try{Thread.sleep(100);}catch(Exception e){}<span style="color:#ff0000;">while</span>(flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"......."+"消费"+"......."+this.num);flag=true;this.notify();}}运行结果如下:
然后发现,倒是生产一个消费一个了,但是只运行到1就不运行了,为什么呢?死锁!!!!
即在上述第5步中,t2被唤醒了,然后因为while判断flag为false,其也进入wait中,结果t1、t2、t3、t4均处于等待状态,则相当于死锁!
解决办法:notifyAll(),全部唤醒!
代码如下:
public class Factory {private int num;private int value;private boolean flag=true;public synchronized void setMessage(int num,int value){try{Thread.sleep(10);}catch(Exception e){}while(!flag)try{this.wait();}catch(Exception e){}this.num=num;this.value=value;System.out.println(Thread.currentThread().getName()+"......."+"生产"+".."+this.num);flag=false;this.notifyAll();}public synchronized void getMessage(){try{Thread.sleep(100);}catch(Exception e){}while(flag)try{this.wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"......."+"消费"+"......."+this.num);flag=true;this.notifyAll();}}
此时正常了!
- Java多线程(3)——多线程通信(传智播客毕老师视频讲解)
- Java多线程(1)——多线程创建(传智播客毕老师视频讲解)
- Java多线程(2)——多线程安全(传智播客毕老师视频讲解)
- Java多线程(4)——多线程JDK5.0升级版(传智播客毕老师视频讲解)
- Java多线程(5)——多线程停止(传智播客毕老师视频讲解)
- Java多线程(6)——Thread类中的一些方法(传智播客毕老师视频讲解)
- Java中泛型(3)——泛型方法(传智播客毕老师视频讲解)
- Java中IO流(3)——字符流缓冲区(传智播客毕老师视频讲解)
- Java中Collection子接口(1)——List接口(传智播客毕老师视频讲解)
- Java中Collection子接口(2)——Set接口(传智播客毕老师视频讲解)
- Java中泛型(1)——泛型介绍(传智播客毕老师视频讲解)
- Java中泛型(2)——泛型类(传智播客毕老师视频讲解)
- Java中泛型(4)——泛型接口(传智播客毕老师视频讲解)
- Java中泛型(5)——泛型限定(传智播客毕老师视频讲解)
- Java中IO流(1)——IO流概述(传智播客毕老师视频讲解)
- Java中IO流(2)——字符流(传智播客毕老师视频讲解)
- Java中IO流(4)——字节流(传智播客毕老师视频讲解)
- Java中IO流(5)——字节流缓冲区(传智播客毕老师视频讲解)
- 22条今生必看的文字
- spring mvc3中的addFlashAttribute方法
- Android下的Handler机制
- 公益404页面-愿2013充满各种正能量!
- Total Commander 批量重命名之位数对齐
- Java多线程(3)——多线程通信(传智播客毕老师视频讲解)
- KEmulator eclipse 调试
- spring3.1 jar依赖关系
- 大智慧F10离线文件下载—大智慧V5.997下载
- WPF中判断滚动条滚动条滑倒了最底端
- C++中qsort对不同类型数组排序
- 开发中遇到字节对齐的问题
- 【1140】面向对象程序设计上机练习一(函数重载)
- Unity3D游戏开发之自由视角状态下的角色控制(二)