Java线程间的通信

来源:互联网 发布:iphone7plus在线软件 编辑:程序博客网 时间:2024/05/01 15:47
线程间的通信



1、当任务相互之间进行协作时,关键问题是实现这些任务之间的握手,为了实现这种握手,可以使用任务相同的基础特性:互斥对于这些实现这些握手问题可以采用的方法:
①通过Object对象的wait()和notify()方法来安全实现
②通过SE5并发类库中Condition对象的await()和signal()方法;


2、Object的wait、notify方法

1)wait、notify和sleep、yield的区别
     ①wait()可以使一个任务被挂起,对象上的锁被释放,而sleep、yield调用时对象上的锁不会被释放
     ②wait、notify是基于Object的,而sleep、yield是基于Thread的;
     ③wait、notify只能在synchronized同步控制方法、或同步控制块中调用(因为需要同步控制锁),sleep、yield可以在费同步控制块中调用;
2)wait(milliseconds):调用对象的锁被挂起milliseconds, 之后对象从wait中恢复;
     wait():调用对象的锁被释放,并无限等待下去,直到该对象调用了notify或notifyAll;
     notifyAll():将调用对象上全部被挂起的任务恢复运行;
     notify():将调用对象上一个被挂起的任务恢复,该任务的选择时任意,一般当多个任务使用相同的条件等待同一个锁,此时使用notify比起notifyAll效率更高;
3)示例代码
T1:
synchronized(sharedMonitor){
     <setup condition for T2>;
     sharedMonitor.notify();
}
T2:
synchronized(shareedMonitor){
     while(somwCondition){
          //point1;
          shareMonitor.wait();
     }
}
//T1是通知T2的线程,T2在运行是由于某种条件而挂起,而T1进行相应的处理后改变该条件,同时释放shareMonitor的锁,是T1恢复运行;
/*如果T2是以下的方式,可能会错失T1的通知,在T2 point1处,由T1处理后的条件状态可能已经被修改,从而可能会导致死锁
while(someCondition){
     //point1;
     synchronized(sharedMonitor)
          shareMonitor.wait();
}*/


实例:仿真延时多输线程输入开关(响应每一个任务):
class Light{     private boolean switch = false;  //线程控制条件     public void turnOn() throws InterruptedException{  //turn on switch          synchronized(this){               while(switch == true)                    wait();                             TimeUnit.SECONDS.sleep(2);  //at lest run 3s;               switch = true;               notifyAll();          }     }     public void trunOff() throws InterruptedException{  //turn off switch          synchronized(this){               while(switch == false)                    wait();               switch = true;               notify();          }     }}//Testclass Demo{     public static void main() throws InterruptedException {          Switch lightSwitch = new Switch();          Scanner input = new Scanner(System.in);          ExecutorService exec = Executor.newCachedThreadPool();          while(!Thread.interrupted()){               in = nextBoolean();               if(in){                    exec.execute(new Runnable(){                         public void run(){                              lightSwitch.turnOn();                     }});               }                                  else if(!in){                    exec.execute(new Runnable(){                         public void run(){                              lightSwitch.turnOff();                     }});               }          }         }}



     
3、Condition的await、signal操控Lock的行为

1)对lock绑定一个或多个Condition条件类,在不同的代码块中通过Condition来操作lock;
一个lock可以对应拥有多个condition,每个condition的状态对应各自的状态控制,共同决定lock的状态
2)使用lock的condition条件进行线程通信时注意要检测InterruptedException异常;
3) signal( ):唤醒任意一个等待该条件的线程;
      signalAll( ):唤醒所有等待该条件的线程;
4)实例代码
class Demo {
 *   Lock lock = new ReentrantLock();
 *   Condition condition = lock.newCondition();
 *   public void method1(){
 *          lock.lock();
 *          try{
 *                while()
 *                   condition.await();
 *                statements;
 *          }catch(InterruptedException ex){
 *          }finally{
 *                lock.unlock();
 *          }
 *    }
 *    public void method2(){   
 *          lock.lock();
 *          try{
 *                statements;
 *                condition.signalAll();
 *          }finally{
 *                lock.unlock();
 *          }
 *    }
 * }/*method1在条件不满时线程被挂起,对象锁释放,
      method2获取对象锁之后,进行相应的操作修改条件,并释放对象锁
      method1再次获取对象锁后,满足条件并执行之后的代码;*/

 
实例:仿真延时多输线程输入开关(响应每一个任务):
class Switch{     Lock lock = new ReentrantLock();     Condition condition = lock.newCondition();     private boolean switch;     public void turnOn(){          lock.lock();          try{               while(switch == true)                    condition.await();                                 TimeUnit.SECONDS.sleep(2);               switch = true;               condition.signalAll();          }catch(InterruptException e){          }finally{               lock.unlock();          }     }     public void turnOff(){          lock.lock();          try{               while(switch == false)                    condition.await();               switch = false;               condition.signalAll();          }catch(InterruptException e){          }finally{               lock.unlock();          }     }  }



4、阻塞队列BlockingQueue<T>

1)wait和notifyAll是以一种比较底层的方式解决任务互操作的问题,可以使用java.util.concurrent.BlockingQueue同步队列这种更高抽象级别的方式来解决这样的问题;
2)BlockingQueue在任何时刻都只允许一个任务加入或删除元素时,即能维持线程同步BlockingQueue为线程安全的;
3)BlockingQueue的主要实现具体类:
    ①ArrayBlockingQueue:有界阻塞队列,当满队时,加入操作的任务会被阻塞,但队空时,删除操作的任务会被阻塞;
    ②LinkedBlockingQueue:无界阻塞队列;
4)BlockingQueue很适合用来实现生产者-消费者模型;
5)实例
//生产者-消费者模型public class Producer implements Runnable{     BlockingQueue<String> queue;     public Producer(queue){ this.queue = queue;}     public void run(){          try{               <statements to product>               String produce = ....;               queue.put(produce);         }catch(InterruptedException e){              <statements while task is blocked>          }     }}public class Consumer implements Runnable{     BlockingQueue<String> queue;     public Consumer(BlockingQueue<String> queue){ this.queue = queue}     public void run(){          try{               String produce = queue.take();               <statements to use produce>          }catch(InterruptException e){               <statements whlie the task is blocked>          }     }}public class Test{     public static void main(){          ExecuteService exec = Executors.newCachedThradPool();          BlockingQueue<String> queue = new LinkedBlockingQueue();          exec.execute(new Producer(queue));          exec.execute(new Consumer(queue));          exec.execute(new Consumer(queue));          exec.shutdown();     }}




5、任务间使用管道进行输入/输出

1)在Java中PipedWriter、PipedReader类库提供线程功能以“管道”的形式对线程间的输入/输出提供了支持;
PipedWriter:允许不同任务任务向管道写入;
PipedReader:允许不同任务从同一个管道读取;
2)示例代码:
import java,io.*;class Sender implementouts Runnable{     private PipedWriter out  = new PipedWriter();     public void run(){          while(true){               out.write('A'+new Random().nextInt(26));          }catch(IOException e){          }catch(InterruptedException e){          }     }}class Receiver implements Runnable{     private PipedReader in = new PipedReader();     private static count;     public void run(){          while(true){               char get = (char)in.read();               System.out.println("Receiver "+ count++ +" Read:"+ get);          }catch(IOException e){          }catch(InterrupedException e){          }     }}class Test{    public static void main(){          ExecutorServcie exec = Executors.newCachedThreadPool();          exec.execute(new Sender());          exec.execute(new Revicer());          exec.execute(new Revicer());          TimeUnit.SECONDS.sleep(20);          exec.shutdownNow();     }}




0 0