006 Java并发编程wait、notify、notifyAll和Condition
原文https://www.cnblogs.com/dolphin0520/p/3920385.html#4182690
Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。
今天我们就来探讨一下Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition
以下是本文目录大纲:
一.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 */publicfinalnativevoidnotify();/** * 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. */publicfinalnativevoidnotifyAll();/** * 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. */publicfinalnativevoidwait(longtimeout) throwsInterruptedException; | 
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的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 | publicclassTest {    publicstaticObject object = newObject();    publicstaticvoidmain(String[] args) {        Thread1 thread1 = newThread1();        Thread2 thread2 = newThread2();                thread1.start();                try{            Thread.sleep(200);        } catch(InterruptedException e) {            e.printStackTrace();        }                thread2.start();    }        staticclassThread1 extendsThread{        @Override        publicvoidrun() {            synchronized(object) {                try{                    object.wait();                } catch(InterruptedException e) {                }                System.out.println("线程"+Thread.currentThread().getName()+"获取到了锁");            }        }    }        staticclassThread2 extendsThread{        @Override        publicvoidrun() {            synchronized(object) {                object.notify();                System.out.println("线程"+Thread.currentThread().getName()+"调用了object.notify()");            }            System.out.println("线程"+Thread.currentThread().getName()+"释放了锁");        }    }} | 
无论运行多少次,运行结果必定是:
二.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()。
三.生产者-消费者模型的实现
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 | publicclassTest {    privateintqueueSize = 10;    privatePriorityQueue<Integer> queue = newPriorityQueue<Integer>(queueSize);         publicstaticvoidmain(String[] args)  {        Test test = newTest();        Producer producer = test.newProducer();        Consumer consumer = test.newConsumer();                 producer.start();        consumer.start();    }         classConsumer extendsThread{                 @Override        publicvoidrun() {            consume();        }                 privatevoidconsume() {            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()+"个元素");                }            }        }    }         classProducer extendsThread{                 @Override        publicvoidrun() {            produce();        }                 privatevoidproduce() {            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 | publicclassTest {    privateintqueueSize = 10;    privatePriorityQueue<Integer> queue = newPriorityQueue<Integer>(queueSize);    privateLock lock = newReentrantLock();    privateCondition notFull = lock.newCondition();    privateCondition notEmpty = lock.newCondition();        publicstaticvoidmain(String[] args)  {        Test test = newTest();        Producer producer = test.newProducer();        Consumer consumer = test.newConsumer();                 producer.start();        consumer.start();    }         classConsumer extendsThread{                 @Override        publicvoidrun() {            consume();        }                 privatevoidconsume() {            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();                }            }        }    }         classProducer extendsThread{                 @Override        publicvoidrun() {            produce();        }                 privatevoidproduce() {            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();                }            }        }    }} | 
参考资料:
《Java编程思想》
http://blog.csdn.net/ns_code/article/details/17225469
http://blog.csdn.net/ghsau/article/details/7481142
006 Java并发编程wait、notify、notifyAll和Condition的更多相关文章
- java 并发——理解 wait / notify / notifyAll
		一.前言 前情简介: java 并发--内置锁 java 并发--线程 java 面试是否有被问到过,sleep 和 wait 方法的区别,关于这个问题其实不用多说,大多数人都能回答出最主要的两点区别 ... 
- Java并发编程_wait/notify和CountDownLatch的比较(三)
		1.wait/notify方法 package sync; import java.util.ArrayList; import java.util.List; public class WaitA ... 
- 【Java并发编程】并发编程大合集-值得收藏
		http://blog.csdn.net/ns_code/article/details/17539599这个博主的关于java并发编程系列很不错,值得收藏. 为了方便各位网友学习以及方便自己复习之用 ... 
- 【Java并发编程】并发编程大合集
		转载自:http://blog.csdn.net/ns_code/article/details/17539599 为了方便各位网友学习以及方便自己复习之用,将Java并发编程系列内容系列内容按照由浅 ... 
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
		Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ... 
- 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
		Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ... 
- Java并发编程:线程间通信wait、notify
		Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ... 
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
		Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ... 
- Java并发编程原理与实战二十一:线程通信wait¬ify&join
		wait和notify wait和notify可以实现线程之间的通信,当一个线程执行不满足条件时可以调用wait方法将线程置为等待状态,当另一个线程执行到等待线程可以执行的条件时,调用notify可以 ... 
随机推荐
- 【题解】CF#983 E-NN country
			首先,我们从 u -> v 有一个明显的贪心,即能向上跳的时候尽量向深度最浅的节点跳.这个我们可以用树上倍增来维护.我们可以认为 u 贪心向上跳后不超过 lca 能跳到 u' 的位置, v 跳到 ... 
- 【题解】HNOI2018转盘
			何学长口中所说的‘一眼题’……然而实际上出出来我大HN全省也只有一个人A…… 首先我们需要发现一个性质:我们永远可以在最后一圈去标记所有的物品.倘若我们反复转圈,那么这完全是可以省下来的.所以我们破环 ... 
- 【BZOJ5301】【CQOI2018】异或序列(莫队)
			[BZOJ5301][CQOI2018]异或序列(莫队) 题面 BZOJ 洛谷 Description 已知一个长度为 n 的整数数列 a[1],a[2],-,a[n] ,给定查询参数 l.r ,问在 ... 
- POJ2142:The Balance——题解
			http://poj.org/problem?id=2142 题目大意:有一天平和两种数量无限的砝码(重为a和b),天平左右都可以放砝码,称质量为c的物品,要求:放置的砝码数量尽量少:当砝码数量相同时 ... 
- BZOJ2668:[CQOI2012]交换棋子——题解
			http://www.lydsy.com/JudgeOnline/problem.php?id=2668 https://www.luogu.org/problemnew/show/P3159#sub ... 
- MyBatis子查询
			一.父查询BaseChildResultMap: <?xml version="1.0" encoding="UTF-8" ?> <!DOCT ... 
- Educational Codeforces Round 60 (Rated for Div. 2) 题解
			Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ... 
- Mobile phones POJ - 1195 二维树状数组求和
			Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows ... 
- springMVC新理解
			springmvc 中@Controller和@RestController的区别 1. Controller, RestController的共同点 都是用来表示spring某个类的是否可以接收HT ... 
- C#中static void Main(string[] args)的含义
			static:是将main方法声明为静态的. void:说明main方法不会返回任何内容. String[]args:这是用来接收命令行传入的参数,String[]是声明args是可以存储字符串数组. ... 
