Java 同步方式 (2) —— wait和notify/notifyall

来源:互联网 发布:谁有淘宝免单的群 编辑:程序博客网 时间:2024/05/31 11:03

Java 中除了关键字 synchronized 能够实现线程同步外,还可以使用 wait 和 notify/notify 实现同步。

wait 方法是使拥有当前对象(object)的线程(thread)放弃锁(release lock),进入睡眠状态

notify 通知该对象(object)因上面调用wait而等待的某一进程重新唤醒启动

notifyAll 通知在对象(object)上因调用wait而等待的所有进程启动,这些进程根据优先级顺序执行


一个线程在其生命周期内总是处于某种状态:

  1. 创建: 当一个线程对象被声明并创建后,它处于“创建”状态;
  2. 就绪:线程对象调用 start() 方法后,将进入“就绪”状态,处于“就绪”状态的线程不是立即执行,而是进入就绪队列,等待CPU;
  3. 运行:当就绪队列中具有最高优先级的就绪线程被调度并获得CPU时,便进入“运行”状态,执行 run() 方法,run 方法中定义了线程的操作和功能;
  4. 非运行:处于“运行”状态的线程可能因为某些原因 (例如人为挂起)进入“非运行”状态,让出CPU并临时中止自己的执行;
  5. 停止:线程完成了它的全部工作或调用 stop() 方法强制中止线程,线程就进入“停止”状态。


wait 与 sleep 区别

wait

sleep

归属类

属于Object类(java.lang.Object)

属于Thread类(java.lang.Thread),静态方法

释放锁

释放了锁,其它线程同步块或方法

没有释放锁,不出让系统资源(如cpu)

中断唤醒

wait一般不会加时间限制,而是判断是否满足符合条件;

如果符合条件,则notify/notifyall唤醒

sleep(milliseconds)后自动唤醒,

如果时间不到可用interrupt()强制中断

适用范围

同步方法或同步块使用(synchronized)

任何地方都可使用(main、thread线程)

捕获异常

必须捕获异常(try/catch)

不需要捕获异常


wait - sleep 示例(区别)

[java] view plaincopyprint?
  1. package com.homer.thread; 
  2.  
  3. public class waitsleep { 
  4.     public staticvoid main(String[] args) { 
  5.         ThreadDemo th = new ThreadDemo(); 
  6.          
  7.         th.start(); 
  8.         System.out.println("thread is starting..."); 
  9.          
  10.         synchronized (th) { 
  11.             try
  12.                 System.out.println("Waiting for th to complete..."); 
  13. //              th.wait(1000);      // 等待1秒后,立刻执行 
  14.                 th.wait();          // 线程等待,notify唤醒后执行 
  15.             } catch (Exception e) { 
  16.                 e.printStackTrace(); 
  17.             } 
  18.             System.out.println("Total is : " + th.total);  // 线程唤醒后,执行 
  19.         } 
  20.     } 
  21.  
  22. class ThreadDemo extends Thread { 
  23.     int total; 
  24.      
  25.     @Override 
  26.     public void run(){ 
  27.         try
  28.             Thread.sleep(2000);    // 睡眠2秒 
  29.             synchronized(this){ 
  30.                 System.out.println("Thread is running..."); 
  31.                 for(int i=0; i<10; i++) { 
  32.                     total += i; 
  33.                     System.out.println("i = " + i +"; total = " + total); 
  34.                      
  35.                     if(i==5) { 
  36.                         System.out.println("i = 5 sleep(3000)"); 
  37.                         Thread.sleep(3000);    // i = 5时,睡眠3秒 
  38.                     } 
  39.                 } 
  40.                 this.notify(); 
  41.             } 
  42.         } catch (Exception e) { 
  43.             e.printStackTrace(); 
  44.         } 
  45.     } 
运行结果:

thread is starting...
Waiting for th to complete...
Thread is running...
i = 0; total = 0
i = 1; total = 1
i = 2; total = 3
i = 3; total = 6
i = 4; total = 10
i = 5; total = 15
i = 5 sleep(3000)
i = 6; total = 21
i = 7; total = 28
i = 8; total = 36
i = 9; total = 45
Total is : 45


================================

wait - notify 示例(生产者 - 消费者)

[java] view plaincopyprint?
  1. package com.homer.thread; 
  2.  
  3. public class waitnotify { 
  4.     public staticvoid main(String[] args) { 
  5.         Q q = new Q(); 
  6.         new Producer(q); 
  7.         new Consumer(q); 
  8.     } 
  9.  
  10. class Producer implements Runnable { 
  11.     Q q = null
  12.      
  13.     public Producer(Q q) { 
  14.         this.q = q; 
  15.         (new Thread(this,"Producer")).start(); 
  16.     } 
  17.      
  18.     @Override 
  19.     public void run() { 
  20.         int i = 0
  21.         while(i<5) { 
  22.             q.put(i++); 
  23.         } 
  24.     } 
  25.  
  26. class Consumer implements Runnable { 
  27.     Q q = null
  28.      
  29.     public Consumer(Q q) { 
  30.         this.q = q; 
  31.         (new Thread(this,"Consumer")).start(); 
  32.     } 
  33.  
  34.     @Override 
  35.     public void run() { 
  36.         while(q.get()<5){ 
  37.         } 
  38.     } 
  39.  
  40. class Q { 
  41.     int n; 
  42.     boolean valueSet = false
  43.      
  44.     public synchronizedint get() { 
  45.         if(!valueSet) {     // if valueSet == false,wait else try to got value 
  46.             try
  47.                 wait(); 
  48.             } catch (InterruptedException e) { 
  49.                 e.printStackTrace(); 
  50.             } 
  51.         }  
  52.          
  53.         System.out.println("Get n : " + n); 
  54.         valueSet = false
  55.         notify(); 
  56.          
  57.         return n; 
  58.     } 
  59.      
  60.     public synchronizedvoid put(int n) { 
  61.         if(valueSet) {      // if valueSet == true,already have value so wait fetch,else put 
  62.             try
  63.                 wait(); 
  64.             } catch (InterruptedException e) { 
  65.                 e.printStackTrace(); 
  66.             } 
  67.         } 
  68.          
  69.         this.n = n; 
  70.         System.out.println("Put n : " + n); 
  71.         valueSet = true
  72.         notify(); 
  73.     } 
运行结果:

Put n : 0
Get n : 0
Put n : 1
Get n : 1
Put n : 2
Get n : 2
Put n : 3
Get n : 3
Put n : 4
Get n : 4


首先两个线程启动,他们的执行占用CPU多少随机,但是这里因为加了一个锁的Boolean型变量,而控制了put与set.

首先:创建了一个对象Q,创建了一个Producer,一个Consumer,这两个对象在构造方法中启动了线程.
第一步:
    对于Producer来说,会首先去调用put方法,因为valueSet是默认值是false,所以在Q的put方法不执行wait 而是执行 this.n = n 赋值操作,执行完毕后设置为valueSet = true
    对于Consumer来说,会首先去调用get方法,因为valueSet是默认值是false,所以该线程会执行wait(等待valueSet 赋值状态为true)
第二步
   对于Producer来说,因为valueSet已经变成true,所以会wat.
   对于Consumer来说,因为valueSet已经变成true,所以会执行下面的code(get value),然后设置valueSet为false.
第三步
   Producer执行put方法,因为valueSet为false
   Consumer等待(重复第一步)
依次类推,方法执行...

这里关键是加了一个共享的变量 valueSet 来判是该取值get,还是put值。当然有了wait跟notify才使它成为了可以实现的。
但是不管怎样,wait是使目前控制该对象(Q的对象q)的线程wait(等待),notify是使前面在该对象上面wait的方法继续执行.


原创粉丝点击