(51)线程间通信,等待唤醒机制

**线程间通讯:
其实就是多个线程在在操作同一资源。
但是操作功能不同**
功能简介:有个资源类,输入类就是向资源类中输入男、女信息,输出类就是输出相匹配的男女信息。创建多个线程,执行代码

public class Res {
     String name;
     String sex;
}
public class Input implements Runnable {

   private Res r;
   Input(Res r){
       this.r=r;
   }

    @Override
    public void run() {
        // TODO Auto-generated method stub
          int x=0;
          while(true) {
              synchronized(r) {
              if(x==0) {

                  r.name="make";
                  r.sex="man";
                  }

              else {

                  r.name="丽丽";
                  r.sex="女女女女女";

               }

              x=(x+1)%2;}
          }
    }

}
public class Output implements Runnable {
     private Res r;
       Output(Res r){
           this.r=r;
       }

    @Override
    public void run() {
          while(true) {
              synchronized(r) {
            System.out.println(r.name+"--------"+r.sex);
              }
          }
    }

}
public class InputOutputDemo {

    public static void main(String[] args) {

        Res r=new Res();
        Input in=new Input(r);
        Output out=new Output(r);
        Thread t1=new Thread(in);
        Thread t2=new Thread(out);
        t1.start();
        t2.start();
    }

}

因为代码较长,所以这里就只说明注意事项
①共享资源是name,sex,所以要将Input、Output类中的run方法相关部分写成同步代码块
②对于synchronized中的参数,因为只有一个共享资源,所以应该用同一把锁,各种创建Object对象,不正确。应该选取不变的东西,可以用r(只要不变的对象就行)
输出结果
从以上的输出结果中可以看出,一组数据会重复打印多次,原因是:
资源类只有两个变量,输入类输入数据,输入线程获得cpu,是个数据 不停覆盖的过程,当输出线程获得cpu后,打印的是输入的最后一组数据,因为输出线程会占一段时间的cpu,所以会打印这个数据多次。

但是这样的话,输出相同数据成片,不太易于观看,那么输入一组数据,打印这组数据,重复进行,该怎么做呢?引入等待唤醒机制
wait:
notify:
notifyall:
都要使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用同步中,因为只有同步才有锁。
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中的线程时,都必须要标识他们所操作线程才有的锁。
只有同一个锁上的被等待线程,可以被同一把锁上的notify唤醒。
也就是说,等待和唤醒必须是同一个锁.
锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中

package com.Thread_communction;
public class Res {
     String name;
     String sex;
     boolean flag=false;
}

public class Input implements Runnable {

   private Res r;
   Input(Res r){
       this.r=r;
   }

    @Override
    public void run() {
        // TODO Auto-generated method stub
          int x=0;
          while(true) {
              synchronized(r) {
                  if(r.flag)
                      try {r.wait();} catch (Exception e) {}
              if(x==0) {

                  r.name="make";
                  r.sex="man";
                  }

              else {

                  r.name="丽丽";
                  r.sex="女女女女女";

               }

              x=(x+1)%2;
              r.flag=true;
              r.notify();
              }
          }
    }

}
public class Output implements Runnable {
     private Res r;
       Output(Res r){
           this.r=r;
       }

    @Override
    public void run() {
          while(true) {
              synchronized(r) {
                  if(!r.flag)
                      try {r.wait();} catch (Exception e) {}
            System.out.println(r.name+"--------"+r.sex);
            r.flag=false;
            r.notify();
              }
          }
    }

}

因为单纯的抽象描述比较难,所以把写好的代码关键部分进行分析:
输入线程:当输入线程执行run后,if(r.flag)为假,不用等待,因为资源池中没数据,输入一组数据,因为该线程会持有一段时间的cpu,所以还会输入覆盖原来的数据,为了避免这种情况,r.flag=true来标识资源区已经有数据,不用再输入数据,唤醒输出线程,可以打印数据,然后继续执行输入线程,判断if(r.flag)为假,等待,等待输出线程输出一组数据后唤醒该输入线程。
**输出线程:**if(!flag)为真,即资源池没数据,因为刚才的输入线程已经置flag为真,所以可以输出数据,相当于资源池为空,即r.flag=false;唤醒输入线程可以输入数据了。
这样就能实现每组数据输出一次,交替输出。

对此代码进行优化:让Res中的成员变量设为private,对外提供访问方法

public class Res {
     private String name;
     private String sex;
     private boolean flag=false;
     public synchronized void set(String  name,String sex) {
         if(flag)
             try {this.wait();} catch (Exception e) {}
         this.name=name;
         this.sex=sex;
         flag=true;
         this.notify();
     }
     public  synchronized  void out() {
         if(!flag)
             try {this.wait();} catch (Exception e) {}
         System.out.println(name+"-------"+sex);
         flag=false;
         this.notify();

     }
}

public class Input implements Runnable {

   private Res r;
   Input(Res r){
       this.r=r;
   }
    public void run() {
          int x=0;
          while(true) {
              synchronized(r) {
              if(x==0) 
                 r.set("make", "man");
              else 
                  r.set("丽丽", "女女女女");
              x=(x+1)%2;

              }
          }
    }

}

public class Output implements Runnable {
     private Res r;
       Output(Res r){
           this.r=r;
       }

    @Override
    public void run() {
          while(true) {
              synchronized(r) {
                  r.out();
              }
          }
    }

}

public class InputOutputDemo {

    public static void main(String[] args) {

        Res r=new Res();
        new Thread(new Input(r)).start();
        new Thread(new Output(r)).start();
    }

}
posted @ 2017-07-12 20:55  测试开发分享站  阅读(88)  评论(0编辑  收藏  举报