reupe

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  sleep()和yield()方法,不会释放锁,而wait()方法会释放当前线程的锁,所以,wait()方法必须是在同步代码块中调用。

 

  • 应用场景

  多个线程合作执行某项任务的时候,有线程A和线程B,它们使用同一对象锁,同一时间它们只有其中之一可以运行,另外一个线程处于等待状态。如下事件图表所示

 

 

 

  •   线程A和线程B使用同一个把锁
  •   线程A工作时,线程B等待,线程B工作时,线程A等待
  •       在4点,线程A调用notify()唤醒线程B,并调用wait()方法,释放锁,任务挂起,这时线程B获取锁继续执行
  •       在11点, 线程B调用notify()唤醒线程A, 并调用wait()方法,释放锁,任务挂起,这时线程A获取锁继续执行

  使用wait()将当前任务挂起,让出CPU资源,比起使用while循环等待符合条件的时机,显然大大节省了CPU的开销。

 

  • wait()使用方式

  wait方法,必须写在同步代码块内,因为底层会使用当前对象的锁。Object.wait()方法的注释中,建议用户在while循环中使用wait,有以下几点原因:

    1.    处于等待中的线程A和线程B被线程C的notifyAll同时唤醒,而线程B还不满足继续执行的条件,下次循环再次挂起;
    2.      因为处于waiting状态中的线程,有可能被虚假唤醒(spurious wakeup),这种唤醒不是通过notify或者interrupt等常规方式,因此,需要继续恢复到wait状态,所以写在while循环中,尽管虚假唤醒发生的概率非           常低。
1  synchronized (obj) {
2         while (<condition does not hold>)
3             obj.wait();
4  // Perform action appropriate to condition
5 } 

  唤醒wait的线程的方法是notify和notifyAll,notify, 两者的区别是,notifyAll唤醒所有等待当前对象锁的线程,notify随机唤醒一个等待当前对象锁的线程。一般而言,使用notifyAll,在wait和while循环配合来决定哪个线程继续执行。

  下面是一个例子,出自《thinking in java》,该程序是给一辆汽车反复打蜡wax、抛光buff,开启两个线程,一个打蜡,一个抛光,顺序是先打蜡再抛光,每次工作过程是:线程A打蜡,线程B等待抛光,线程A打蜡完成后唤醒线程B,线程B开始抛光,线程A等待线程B抛光,线程B抛光完成后唤醒线程A开始打蜡。。。。如此循环5秒钟,由shutdownNow中断所有线程.

 1 class Car {
 2     private boolean waxOn = false;
 3     public synchronized void waxed() {
 4         waxOn = true;   // Read to buff
 5         notifyAll();
 6     }
 7 
 8     public synchronized void buffed() {
 9         waxOn = false;  // Read for another coat of wax
10         notifyAll();
11     }
12 
13     public synchronized void waitForWaxing() throws InterruptedException {
14         while (!waxOn) {
15             wait();
16         }
17     }
18 
19     public synchronized void waitForBuffing() throws InterruptedException {
20         while (waxOn) {
21             wait();
22         }
23     }
24 }
25 
26 class WaxOn implements Runnable {
27     private Car car;
28 
29     public WaxOn(Car car) {
30         this.car = car;
31     }
32 
33     @Override
34     public void run() {
35         try {
36             while (!Thread.interrupted()) {
37                 System.out.println("Wax On! ");
38                 TimeUnit.MILLISECONDS.sleep(200);
39                 car.waxed();
40                 car.waitForBuffing();   // buffing完了才能下一次wax
41             }
42         } catch (InterruptedException e) {
43             System.out.println("Exiting via interrupt");
44         }
45         System.out.println("Ending Wax On task!");
46     }
47 }
48 
49 class WaxOff implements Runnable {
50     private Car car;
51 
52     public WaxOff(Car car) {
53         this.car = car;
54     }
55 
56     @Override
57     public void run() {
58         try {
59             while (!Thread.interrupted()) {
60                 car.waitForWaxing();    // wax完后才能buff
61                 System.out.println("Wax Off! ");
62                 TimeUnit.MICROSECONDS.sleep(200);
63                 car.buffed();
64             }
65         } catch (InterruptedException e) {
66             System.out.println("Exiting via interrupt");
67         }
68         System.out.println("Ending Wax Off task");
69     }
70 }
71 
72 
73 public class WaxOMatic {
74     public static void main(String[] args) throws InterruptedException {
75         Car car = new Car();
76         ExecutorService exec = Executors.newCachedThreadPool();
77         exec.execute(new WaxOn(car));
78         exec.execute(new WaxOff(car));
79         TimeUnit.SECONDS.sleep(5);
80         exec.shutdownNow();
81     }
82 }

 

  •  防止死锁发生

  如下这种情况,如果线程A运行到了point1, 此时CPU调动切换到线程B,线程B进入上面的代码块,执行了notify,而此时线程A还未进入同步代码块,这时候就错过一次notify信号了,这时候进入同步代码块,就可能发生死锁永远无法被唤醒了。  

synchronized (monitor) {
     // action
     monitor.notify();
}


while (<condition>) {
     // point1
     synchronized (monitor) {
         monitor.wait();
     }
}

  如果要防止死锁,应该将while写在同步代码块之内

synchronized (monitor) {
       // action
       monitor.notify();
}

synchronized (monitor) {
     while (<condition>) {
        monitor.wait();
     }
}

 

 

 

  

posted on 2020-01-17 15:02  yxlaisj  阅读(1206)  评论(0编辑  收藏  举报