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();}}

此时正常了!




0 0
原创粉丝点击