线程通信-等待和唤醒机制和锁(Lock)机制

来源:互联网 发布:推广数据留言 编辑:程序博客网 时间:2024/05/21 08:00

生产者和消费者案例分析:

经典的生产者和消费者案例(Producer/Consumer):

     分析案例:

              1):生产者和消费者应该操作共享的资源(实现方式来做).

              2):使用一个或多个线程来表示生产者(Producer).

              3):使用一个或多个线程来表示消费者(Consumer).

生产者消费者的示意图:


为什么生产者不直接把数据给消费者,而是先把数据存储到共享资源中,然后,消费者再从共享资源中取出数据,再消费.

在这里体现了面向对象的设计理念:低耦合.

       ()耦合:直接使用生产者把肉包子给消费者,那么生产者中得存在消费者的引用,同理,消费者要消费生产者生产的肉包子,消费者中也得存在生产者对象的引用.例子: 主板和集成显卡.

       ()耦合:使用一个中间对象,屏蔽了生产者和消费者直接的数据交互.  例子:主板和独立显卡.

()耦合:

//生产者public class  Producer{     private  Consumer  con;//消费者对象}//消费者public  class    Consumer{        private  Producer  pro;//消费者对象}

()耦合:

//共享资源public  class ShareResource{}//生产者public class  Producer{       private  ShareResource  resource;//共享资源对象}//消费者public  class    Consumer{         private  ShareResource  resource;//共享资源对象}

实现生产者和消费者案例

生产者:

//生产者生产数据public class Producer implements Runnable{private ShareResource resource = null;public Producer(ShareResource resource){this.resource = resource;}public void run() {for(int i = 0; i < 50; i++){if(i % 2 == 0){resource.push("春哥", "男");}else{resource.push("凤姐", "女");}}}}

消费者:

//消费者消费类public class Consumer implements Runnable{private ShareResource resource = null;public Consumer(ShareResource resource){this.resource = resource;}public void run() {for(int i = 0; i < 50; i ++){resource.popup();}}}

共享资源类:

//生产者和消费者共同的资源对象public class ShareResource {private String name;private String gender; //一般用枚举来写性别,这里先用String/** * 生产者向共享资源存储数据 * @param name 生产者生产的姓名 * @param gender 生产者生产的性别 */public void push(String name,String gender){this.name = name;Thread.sleep(10);this.gender = gender;}/** * 消费者向共享资源获取数据 */ public void popup(){System.out.println(this.name + "-" + this.gender);}}

分析生产者和消费者案例存在的问题:

  建议在生产姓名和性别之间以及在打印之前使用Thread.sleep(10);使效果更明显.

  此时出现下面的情况:


问题1:出现姓别紊乱,即出现凤姐-男的情况.

      生产者先生产出春哥哥-,此时消费者没有消费,生产者继续生产出姓名为凤姐,此时消费者开始消费了.

      解决方案:只要保证在生产姓名和性别的过程保持同步,中间不能被消费者线程进来取走数据.

      可以使用同步代码块/同步方法/Lock机制来保持同步性.

问题2:重复出现凤姐-男和凤姐-女,应该出现生产一个数据,消费一个数据.

      应该交替出现:  春哥哥--->凤姐--->春哥哥--->凤姐-.....

      解决方案: 得使用 等待和唤醒机制.


线程通信:不同的线程执行不同的任务,如果这些任务有某种关系,线程之间必须能够通信,协调完成工作.

等待和唤醒机制:

线程通信-waitnotify方法介绍:

java.lang.Object类提供类两类用于操作线程通信的方法.

wait():执行该方法的线程对象释放同步锁,JVM把该线程存放到等待池中,等待其他的线程唤醒该线程.

notify:执行该方法的线程唤醒在等待池中等待的任意一个线程,把线程转到锁池中等待.

notifyAll():执行该方法的线程唤醒在等待池中等待的所有的线程,把线程转到锁池中等待.

注意:上述方法只能被同步监听锁对象来调用,否则报错IllegalMonitorStateException..

------------------------------------------

假设A线程和B线程共同操作一个X对象(同步锁),A,B线程可以通过X对象的waitnotify方法来进行通信,流程如下:

1:A线程执行X对象的同步方法时,A线程持有X对象的锁,B线程没有执行机会,B线程在X对象的锁池中等待.

2:A线程在同步方法中执行X.wait()方法时,A线程释放X对象的锁,进入A线程进入X对象的等待池中.

3:X对象的锁池中等待锁的B线程获取X对象的锁,执行X的另一个同步方法.

4:B线程在同步方法中执行X.notify()方法时,JVMA线程从X对象的等待池中移动到X对象的锁池中,等待获取锁.

5:B线程执行完同步方法,释放锁.A线程获得锁,继续执行同步方法.

 

因此,生产者消费者问题中的共享资源对象类可改为:

//生产者和消费者共同的资源对象public class ShareResource {private String name;private String gender;  //一般用枚举来写性别,这里先用Stringprivate boolean isEmpty = true;/** * 生产者向共享资源存储数据 * @param name 生产者生产的姓名 * @param gender 生产者生产的性别 */synchronized public void push(String name,String gender){try {while(!isEmpty){//isEmpty为false时,当前线程等待//if(!isEmpty)用if也可以达到效果,只是while更安全(至少判断两次)this.wait();}this.name = name;Thread.sleep(10);this.gender = gender;isEmpty = false;//设置共享资源不为空this.notify();  //唤醒其他任意一个线程,这里是唤醒一个消费者} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}/** * 消费者向共享资源获取数据 */ synchronized public void popup(){try {while(isEmpty){//当isEmpty为true时,共享资源为空,消费者等待this.wait();Thread.sleep(10);}System.out.println(this.name + "-" + this.gender);isEmpty = true;//消费结束,资源应设为空this.notify();// 唤醒一个生产者} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

除了等待唤醒机制,也可以使用锁机制

线程通信-使用LockCondition接口:

waitnotify方法,只能被同步监听锁对象来调用,否则报错IllegalMonitorStateException.

那么现在问题来了,Lock机制根本就没有同步锁了,也就没有自动获取锁和自动释放锁的概念.

因为没有同步锁,所以Lock机制不能调用waitnotify方法.

解决方案:Java5中提供了Lock机制的同时提供了处理Lock机制的通信控制的Condition接口.

--------------------------------------------------------------------

Java5开始,可以:

      1):使用Lock机制取代synchronized代码块和synchronized方法.

      2):使用Condition接口对象的await,signal,signalAll方法取代Object类中的wait,notify,notifyAll方法.


共享资源类可改成:

//生产者和消费者共同的资源对象public class ShareResource {private String name;private String gender;private boolean isEmpty = true;private final Lock lock = new ReentrantLock();private final Condition condition = lock.newCondition();/** * 生产者向共享资源提供数据 * @param name 生产者生产的名字 * @param gender 性别 */public void push(String name,String gender){lock.lock();try {if(!isEmpty){condition.await();}this.name = name;Thread.sleep(10);this.gender = gender;isEmpty = false;condition.signal();} catch (InterruptedException e) {e.printStackTrace();}finally{lock.unlock();}}/** * 消费者从共享资源获取(消费)数据 */public void popoup(){lock.lock();try {if(isEmpty){condition.await();}Thread.sleep(10);System.out.println(name+"-"+gender);isEmpty = true;condition.signal();} catch (InterruptedException e) {e.printStackTrace();}finally{lock.unlock();}}}



1 0
原创粉丝点击