Java多线程-Object.wait()

摘要:
wait()方法将释放当前线程的锁。必须在同步代码块中调用wait()方法。线程A调用notify()来唤醒线程B,线程B调用notify()来叫醒线程A。此时,线程A获取一个锁以继续执行,并使用wait()来暂停当前任务。等待中的线程A和线程B同时被线程C的notify All唤醒。

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

  • 应用场景

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

Java多线程-Object.wait()第1张

  •   线程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();
     }
}

  

免责声明:文章转载自《Java多线程-Object.wait()》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇document.getElementById('file').files[0]的jquery写法长沙社区团购独角兽《兴盛优选》 18k 面试题记录,已拿offer!下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

(1)java虚拟机概念和结构图

java虚拟机解构图一 java虚拟机解构图二 java虚拟机结构图三 【1】类加载系统--->负责从文件系统或网络中加载class信息,存放至方法区的内存空间【2】java堆--->虚拟机启动时建立。java主要内存工作区域。--->存放:java对象实例。--->所有线程共享【3】方法区--->存放:class信息,...

API Monitor简介(API监控工具)

API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得了API调用。 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题。 64位支持 API监控支持监控的64位应用程序和服务。 64位版本只能用来监视64位应用程序和32位版本仅可用于监测的32位应用程序。 要监视在64位Windows的32位应用...

见到的一篇IOCP流程 自己用demo实现了一下, 简单照抄,改动了一点点

要分析的实例分为两个线程: 分别是主线程(MAIN),还有一个是创建的线程(ServerThread) 1.主函数完成初始化工作:   1.1: (主线程)HANDLE hCompletion = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);    创建完成端口对象   1.2: (主线程...

GDB常用调试命令以及多进程多线程调试

http://blog.csdn.net/freeelinux/article/details/53700266 一:普通命令   1.list命令 list  linenum      显示程序第linenum行周围的程序 list  function      显示函数名为function的函数的源程序 list                  ...

C#基础系列——多线程 信号量 异步 编程 Task Thread async和await

 多线程: ThreadStart 是一个委托函数 static void Main(string[] args) { Thread oGetArgThread = new Thread(new ThreadStart(() => {...

ios 多线程

转自:http://www.maxiaoguo.com/clothes/254.html 多线程包含:GCD  NSOperation   NSOperation是在GCD语言的基础上开发的,GCD类C语言, NSOperation OC语法 GCD: 名词解释  并行 dispatch_queue_t q = dispatch_queue_crea...