黑马程序员 ---- 线程间通信

来源:互联网 发布:怎样下载plc编程软件 编辑:程序博客网 时间:2024/05/21 17:34

  ------- android培训、java培训、期待与您交流! ----------


例1:问题的引出。(共享数据没有同步) 【误】class P{String name;String sex;}class Producer implements Runnable{P q = null;public Producer(P q){this.q = q;}public void run(){int i = 0;while(true){if(i==0){q.name = "Zhang";q.sex = "mail";}else{q.name = "LI";q.sex = "femail";}i = (i+1)%2;// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。}}}class Consumer implements Runnable{P q = null;public Consumer(P q){this.q = q;}public void run(){while(true){System.out.println(q.name+"---------"+q.sex);}}}class Test{public static void main(String[] args){P k = new P();Producer i = new Producer(k);Consumer o = new Consumer(k);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();//new Thread(new Producer(q)).start();//new Thread(new Consumer(q)).start();}}

运行结果:   该程序有问题。 没有同步操作共享数据的代码。可能会出现 name 与 sex 不对应的情况。


例2: 问题的解决。(共享数据同步,加入睡眠唤醒机制,但代码没有优化)     【正】class P{String name;String sex;boolean flag = false;}class Producer implements Runnable{P q = null;public Producer(P q){this.q = q;}public void run(){int i = 0;while(true){synchronized(q)     // 锁可以是 Producer.class 、 Consumer.class 、 q 、 Test.class  这些都是唯一的。{if(q.flag)try{q.wait();}catch(Exception e){}if(i==0){q.name = "Zhang";q.sex = "mail";}else{q.name = "LI";q.sex = "femail";}i = (i+1)%2;// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。q.flag = true;q.notify();}}}}class Consumer implements Runnable{P q = null;public Consumer(P q){this.q = q;}public void run(){while(true){synchronized(q){if(!q.flag)try{q.wait();}catch(Exception e){}System.out.println(q.name+"---------"+q.sex);q.flag = false;q.notify();}}}}class Test{public static void main(String[] args){P k = new P();Producer i = new Producer(k);Consumer o = new Consumer(k);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();//new Thread(new Producer(q)).start();//new Thread(new Consumer(q)).start();}}

运行结果: 该程序正常运行,并得出正确结果。对应的两组 name 、 sex 交替出现。


范例分析: 1. wait(). notify(). notifyAll() 都是用在同步中,因为要对持有的监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。

                 2. 为什么这些操作线程的方法要定义 Object 类中呢
                     因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上notify唤醒。不可以
               对不同锁中的线程唤醒, 也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object 类中。 


例3: 代码优化。(优化代码,但只是用于一个消费者,一个生产者,多个消费者、生产者就不适用了)     【正】class P{private String name;private String sex;private boolean flag = false;public synchronized void set(String name, String sex){if(flag)try{wait();}catch(Exception e){}this.name = name;this.sex = sex;flag = true;this.notify();}public synchronized void out(){if(!flag)try{wait();}catch(Exception e){}System.out.println(name+"\t"+sex);flag = false;this.notify();}}class Producer implements Runnable{P q = null;public Producer(P q){this.q = q;}public void run(){int i = 0;while(true){if(i==0){q.set("zhang","mail");}else{q.set("LI","femail");}i = (i+1)%2;// 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。}}}class Consumer implements Runnable{P q = null;public Consumer(P q){this.q = q;}public void run(){while(true){q.out();}}}class Test{public static void main(String[] args){P q = new P();/*Producer i = new Producer(k);Consumer o = new Consumer(k);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();*/new Thread(new Producer(q)).start();new Thread(new Consumer(q)).start();}}

运行结果: 该程序正确,并得出正确结果。

范例分析: 优点: 在例2 的基础上采取了优化措施,并封装了代码私有后更简洁更安全。
           缺点: 只是用于一个生产者,一个消费者。
             

例4:延续上个程序的需求,但要加入多个生产者和多个消费者.class P{private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name){while(flag)            // 此处用 while 循环是为了让判断过并在此等待的线程,在被唤醒时再判断一次,以免在唤醒时被同是生产者或同是消费者给唤醒。                 try{wait();}catch(Exception e){}this.name = name+"....."+count++;System.out.println(Thread.currentThread().getName()+"-----Producer--------"+count);flag = true;this.notifyAll();       // 一个线程在执行完后,为了不唤醒被类中的线程,而让所有线程都被唤醒,让后用 while 再次判断是否该唤醒需要唤醒的线程。                                 // 当只有一个 Producer 和 一个 Consumer 时, 可以用 notify() ,而当有多个时,用 notifyAll(),来唤醒所有在等待的线程。    }                           // 如果这里使用 notify ,程序在运行一段时间后自动就停止了,因为没有被唤醒的线程没有执行权,而有执行权的线程却没被唤醒。public synchronized void out(){while(!flag)try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"+++++++++++++Consumer+++++"+count);flag = false;this.notifyAll();}}class Producer implements Runnable{P q = null;public Producer(P q){this.q = q;}public void run(){int i = 0;while(true){q.set("水果糖");}}}class Consumer implements Runnable{P q = null;public Consumer(P q){this.q = q;}public void run(){while(true){q.out();}}}class Test{public static void main(String[] args){P q = new P();Producer p = new Producer(q);Consumer c = new Consumer(q);Thread t1 = new Thread(p);          // 定义了四个生产者和四个消费者Thread t2 = new Thread(p);Thread t3 = new Thread(c);Thread t4 = new Thread(c);t1.start();t2.start();t3.start();t4.start();}}

运行结果: 该程序正确,并得出正确结果。




范例分析 ;  优点: 在例3的基础上让程序可以同时有多个生产者和多个消费者。
             缺点: 不适用于一个消费者,一个生产者。

0 0
原创粉丝点击