线程间协作的两种方式:wait、notify、notifyAll和Condition

    xiaoxiao2021-03-25  96

    以下是本文目录大纲:

      一.wait()、notify()和notifyAll()

      二.Condition

      三.生产者-消费者模型的实现

      若有不正之处请多多谅解,并欢迎批评指正。

      请尊重作者劳动成果,转载请标明原文链接:

      http://www.cnblogs.com/dolphin0520/p/3920385.html

     

    一.wait()、notify()和notifyAll()

      wait()、notify()和notifyAll()是Object类中的方法:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /**   * Wakes up a single thread that is waiting on this object's   * monitor. If any threads are waiting on this object, one of them   * is chosen to be awakened. The choice is arbitrary and occurs at   * the discretion of the implementation. A thread waits on an object's   * monitor by calling one of the wait methods   */ public  final  native  void  notify();   /**   * Wakes up all threads that are waiting on this object's monitor. A   * thread waits on an object's monitor by calling one of the   * wait methods.   */ public  final  native  void  notifyAll();   /**   * Causes the current thread to wait until either another thread invokes the   * {@link java.lang.Object#notify()} method or the   * {@link java.lang.Object#notifyAll()} method for this object, or a   * specified amount of time has elapsed.   * <p>   * The current thread must own this object's monitor.   */ public  final  native  void  wait( long  timeout)  throws  InterruptedException;

       从这三个方法的文字描述可以知道以下几点信息:

      1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

      2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

      3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程,唤醒哪个线程并不确定,并且由该线程去获得锁;

      4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程,但是哪个线程获得锁还不一定

         wait和sleep的区别:

         1. sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

         2. 如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此必须在锁中执行(例如在Synchronized代码块中执行)

         sleep和yield的区别:

        ① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;       ② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;       ③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;       ④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

      有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。

      上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

    下面看一个例子就明白了:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public  class  Test {      public  static  Object object =  new  Object();      public  static  void  main(String[] args) {          Thread1 thread1 =  new  Thread1();          Thread2 thread2 =  new  Thread2();                    thread1.start();                    try  {              Thread.sleep( 200 );          }  catch  (InterruptedException e) {              e.printStackTrace();          }                    thread2.start();      }            static  class  Thread1  extends  Thread{          @Override          public  void  run() {              synchronized  (object) {                  try  {                      object.wait();                  }  catch  (InterruptedException e) {                  }                  System.out.println( "线程" +Thread.currentThread().getName()+ "获取到了锁" );              }          }      }            static  class  Thread2  extends  Thread{          @Override          public  void  run() {              synchronized  (object) {                  object.notify();                  System.out.println( "线程" +Thread.currentThread().getName()+ "调用了object.notify()" );                 System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");             }          }      } }

       无论运行多少次,运行结果必定是:

    线程Thread-1调用了object.notify() 线程Thread-1释放了锁 线程Thread-0获取到了锁

    二.Condition

      Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

    Condition是个接口,基本的方法就是await()和signal()方法;Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()  调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用

      Conditon中的await()对应Object的wait();

      Condition中的signal()对应Object的notify();

      Condition中的signalAll()对应Object的notifyAll()。

    Condition的特性:

        1.Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是需要与互斥锁/共享锁捆绑使用的。

        2.Condition它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。     例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。      

          如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。  但是,通过Condition,就能明确的指定唤醒读线程。

    三.生产者-消费者模型的实现

    1.使用Object的wait()和notify()实现:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public  class  Test {      private  int  queueSize =  10 ;      private  PriorityQueue<Integer> queue =  new  PriorityQueue<Integer>(queueSize);             public  static  void  main(String[] args)  {          Test test =  new  Test();          Producer producer = test. new  Producer();          Consumer consumer = test. new  Consumer();                     producer.start();          consumer.start();      }             class  Consumer  extends  Thread{                     @Override          public  void  run() {              consume();          }                     private  void  consume() {              while ( true ){                  synchronized  (queue) {                      while (queue.size() ==  0 ){                          try  {                              System.out.println( "队列空,等待数据" );                              queue.wait();                          }  catch  (InterruptedException e) {                              e.printStackTrace();                              queue.notify();                          }                      }                      queue.poll();           //每次移走队首元素                      queue.notify();                      System.out.println( "从队列取走一个元素,队列剩余" +queue.size()+ "个元素" );                  }              }          }      }             class  Producer  extends  Thread{                     @Override          public  void  run() {              produce();          }                     private  void  produce() {              while ( true ){                  synchronized  (queue) {                      while (queue.size() == queueSize){                          try  {                              System.out.println( "队列满,等待有空余空间" );                              queue.wait();                          }  catch  (InterruptedException e) {                              e.printStackTrace();                              queue.notify();                          }                      }                      queue.offer( 1 );         //每次插入一个元素                      queue.notify();                      System.out.println( "向队列取中插入一个元素,队列剩余空间:" +(queueSize-queue.size()));                  }              }          }      } }

     2.使用Condition实现

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 public  class  Test {      private  int  queueSize =  10 ;      private  PriorityQueue<Integer> queue =  new  PriorityQueue<Integer>(queueSize);      private  Lock lock =  new  ReentrantLock();      private  Condition notFull = lock.newCondition();      private  Condition notEmpty = lock.newCondition();            public  static  void  main(String[] args)  {          Test test =  new  Test();          Producer producer = test. new  Producer();          Consumer consumer = test. new  Consumer();                     producer.start();          consumer.start();      }             class  Consumer  extends  Thread{                     @Override          public  void  run() {              consume();          }                     private  void  consume() {              while ( true ){                  lock.lock();                  try  {                      while (queue.size() ==  0 ){                          try  {                              System.out.println( "队列空,等待数据" );                              notEmpty.await();                          }  catch  (InterruptedException e) {                              e.printStackTrace();                          }                      }                      queue.poll();                 //每次移走队首元素                      notFull.signal();            // 只唤醒生产者线程                      System.out.println( "从队列取走一个元素,队列剩余" +queue.size()+ "个元素" );                  }  finally {                      lock.unlock();                  }              }          }      }             class  Producer  extends  Thread{                     @Override          public  void  run() {              produce();          }                     private  void  produce() {              while ( true ){                  lock.lock();                  try  {                      while (queue.size() == queueSize){                          try  {                              System.out.println( "队列满,等待有空余空间" );                              notFull.await();                          }  catch  (InterruptedException e) {                              e.printStackTrace();                          }                      }                      queue.offer( 1 );         //每次插入一个元素                      notEmpty.signal();                      System.out.println( "向队列取中插入一个元素,队列剩余空间:" +(queueSize-queue.size()));                  }  finally {                      lock.unlock();                  }              }          }      } }
    转载请注明原文地址: https://ju.6miu.com/read-13446.html

    最新回复(0)