java中使用wait()和notify()的简单场景
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2536692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
A simple scenario using wait() and notify() in java
提问by Olaseni
Can I get a complete simple scenario i.e. tutorial that suggest how this should be used, specifically with a Queue?
我可以获得一个完整的简单场景,即建议如何使用它的教程,特别是与队列一起使用?
采纳答案by Jared Russell
The wait()
and notify()
methods are designed to provide a mechanism to allow a thread to block until a specific condition is met. For this I assume you're wanting to write a blocking queue implementation, where you have some fixed size backing-store of elements.
的wait()
和notify()
方法被设计为提供一种机制,以允许一个线程块,直到一个特定的条件被满足。为此,我假设您想要编写一个阻塞队列实现,其中您有一些固定大小的元素后备存储。
The first thing you have to do is to identify the conditions that you want the methods to wait for. In this case, you will want the put()
method to block until there is free space in the store, and you will want the take()
method to block until there is some element to return.
您必须做的第一件事是确定您希望方法等待的条件。在这种情况下,您将希望该put()
方法阻塞直到存储中有可用空间,并且您希望该take()
方法阻塞直到有一些元素要返回。
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
There are a few things to note about the way in which you must use the wait and notify mechanisms.
关于必须使用等待和通知机制的方式,有几点需要注意。
Firstly, you need to ensure that any calls to wait()
or notify()
are within a synchronized region of code (with the wait()
and notify()
calls being synchronized on the same object). The reason for this (other than the standard thread safety concerns) is due to something known as a missed signal.
首先,您需要确保对wait()
或 的任何调用notify()
都在代码的同步区域内(并且wait()
和notify()
调用在同一对象上同步)。造成这种情况的原因(除了标准的线程安全问题)是由于丢失信号。
An example of this, is that a thread may call put()
when the queue happens to be full, it then checks the condition, sees that the queue is full, however before it can block another thread is scheduled. This second thread then take()
's an element from the queue, and notifies the waiting threads that the queue is no longer full. Because the first thread has already checked the condition however, it will simply call wait()
after being re-scheduled, even though it could make progress.
这方面的一个例子是,一个线程可能会put()
在队列已满时调用,然后检查条件,看到队列已满,但是在它可以阻塞另一个线程之前被调度。然后,第二个线程take()
是队列中的一个元素,并通知等待线程队列不再满。然而,因为第一个线程已经检查了条件,它会wait()
在重新调度后简单地调用,即使它可以取得进展。
By synchronizing on a shared object, you can ensure that this problem does not occur, as the second thread's take()
call will not be able to make progress until the first thread has actually blocked.
通过在共享对象上进行同步,您可以确保不会发生此问题,因为在第take()
一个线程实际阻塞之前,第二个线程的调用将无法取得进展。
Secondly, you need to put the condition you are checking in a while loop, rather than an if statement, due to a problem known as spurious wake-ups. This is where a waiting thread can sometimes be re-activated without notify()
being called. Putting this check in a while loop will ensure that if a spurious wake-up occurs, the condition will be re-checked, and the thread will call wait()
again.
其次,由于称为虚假唤醒的问题,您需要将要检查的条件放在 while 循环中,而不是 if 语句中。这是等待线程有时可以在不notify()
被调用的情况下重新激活的地方。将此检查置于 while 循环中将确保如果发生虚假唤醒,则将重新检查条件,并且线程将wait()
再次调用。
As some of the other answers have mentioned, Java 1.5 introduced a new concurrency library (in the java.util.concurrent
package) which was designed to provide a higher level abstraction over the wait/notify mechanism. Using these new features, you could rewrite the original example like so:
正如其他一些答案所提到的,Java 1.5 引入了一个新的并发库(在java.util.concurrent
包中),旨在为等待/通知机制提供更高级别的抽象。使用这些新功能,您可以像这样重写原始示例:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Of course if you actually need a blocking queue, then you should use an implementation of the BlockingQueueinterface.
当然,如果您确实需要一个阻塞队列,那么您应该使用BlockingQueue接口的实现 。
Also, for stuff like this I'd highly recommend Java Concurrency in Practice, as it covers everything you could want to know about concurrency related problems and solutions.
此外,对于此类内容,我强烈推荐Java Concurrency in Practice,因为它涵盖了您可能想了解的有关与并发相关的问题和解决方案的所有内容。
回答by extraneon
Have you taken a look at this Java Tutorial?
你看过这个Java 教程吗?
Further, I'd advise you to stay the heck away from playing with this kind of stuff in real software. It's good to play with it so you know what it is, but concurrency has pitfalls all over the place. It's better to use higher level abstractions and synchronized collections or JMS queues if you are building software for other people.
此外,我建议您不要在真正的软件中玩这种东西。玩它很好,所以你知道它是什么,但并发性到处都是陷阱。如果您正在为其他人构建软件,最好使用更高级别的抽象和同步集合或 JMS 队列。
That is at least what I do. I'm not a concurrency expert so I stay away from handling threads by hand wherever possible.
这至少是我所做的。我不是并发专家,所以我尽可能避免手动处理线程。
回答by polygenelubricants
Even though you asked for wait()
and notify()
specifically, I feel that this quote is still important enough:
即使你要求wait()
和notify()
具体而言,我觉得这句话仍然是足够重要:
Josh Bloch, Effective Java 2nd Edition, Item 69: Prefer concurrency utilities to wait
and notify
(emphasis his):
Josh Bloch,Effective Java 2nd Edition,Item 69:Prefer concurrency utility to wait
and notify
(强调他的):
Given the difficulty of using
wait
andnotify
correctly, you should use the higher-level concurrency utilities instead[...] usingwait
andnotify
directly is like programming in "concurrency assembly language", as compared to the higher-level language provided byjava.util.concurrent
. There is seldom, if ever, reason to usewait
andnotify
in new code.
考虑到使用
wait
和notify
正确使用的困难,您应该使用更高级别的并发实用程序,而不是[...]直接使用wait
和notify
就像在“并发汇编语言”中编程,与java.util.concurrent
. 很少有理由在新代码中使用wait
和notify
。
回答by Enno Shioji
Not a queue example, but extremely simple :)
不是队列示例,但非常简单:)
class MyHouse {
private boolean pizzaArrived = false;
public void eatPizza(){
synchronized(this){
while(!pizzaArrived){
wait();
}
}
System.out.println("yumyum..");
}
public void pizzaGuy(){
synchronized(this){
this.pizzaArrived = true;
notifyAll();
}
}
}
Some important points:
1) NEVER do
一些重要的点:
1)永远不要做
if(!pizzaArrived){
wait();
}
Always use while(condition), because
总是使用 while(condition),因为
- a) threads can sporadically awake from waiting state without being notified by anyone. (even when the pizza guy didn't ring the chime, somebody would decide try eating the pizza.).
- b) You should check for the
condition again after acquiring the
synchronized lock. Let's say pizza
don't last forever. You awake,
line-up for the pizza, but it's not
enough for everybody. If you don't
check, you might eat paper! :)
(probably better example would be
while(!pizzaExists){ wait(); }
.
- a) 线程可以在没有任何人通知的情况下偶尔从等待状态中唤醒。(即使比萨饼人没有按铃,有人会决定尝试吃比萨饼。)。
- b) 获取同步锁后,应再次检查条件。让我们说披萨不会永远持续下去。你醒了,排队买披萨,但对每个人来说还不够。如果你不检查,你可能会吃纸!:)(可能更好的例子是
while(!pizzaExists){ wait(); }
.
2) You must hold the lock (synchronized) before invoking wait/nofity. Threads also have to acquire lock before waking.
2)在调用wait/nofity之前,您必须持有锁(同步)。线程也必须在唤醒之前获取锁。
3) Try to avoid acquiring any lock within your synchronized block and strive to not invoke alien methods (methods you don't know for sure what they are doing). If you have to, make sure to take measures to avoid deadlocks.
3) 尽量避免在您的同步块中获取任何锁并努力不调用外来方法(您不确定它们在做什么的方法)。如果必须这样做,请确保采取措施避免死锁。
4) Be careful with notify(). Stick with notifyAll() until you know what you are doing.
4) 注意notify()。坚持使用 notifyAll() 直到您知道自己在做什么。
5)Last, but not least, read Java Concurrency in Practice!
5) 最后,但并非最不重要,阅读Java 并发实践!
回答by Ferhat KO?ER
Example
例子
public class myThread extends Thread{
@override
public void run(){
while(true){
threadCondWait();// Circle waiting...
//bla bla bla bla
}
}
public synchronized void threadCondWait(){
while(myCondition){
wait();//Comminucate with notify()
}
}
}
public class myAnotherThread extends Thread{
@override
public void run(){
//Bla Bla bla
notify();//Trigger wait() Next Step
}
}
回答by srinivas
Example for wait() and notifyall() in Threading.
线程中的 wait() 和 notifyall() 示例。
A synchronized static array list is used as resource and wait() method is called if the array list is empty. notify() method is invoked once a element is added for the array list.
同步的静态数组列表用作资源,如果数组列表为空,则调用 wait() 方法。一旦为数组列表添加元素,就会调用 notify() 方法。
public class PrinterResource extends Thread{
//resource
public static List<String> arrayList = new ArrayList<String>();
public void addElement(String a){
//System.out.println("Add element method "+this.getName());
synchronized (arrayList) {
arrayList.add(a);
arrayList.notifyAll();
}
}
public void removeElement(){
//System.out.println("Remove element method "+this.getName());
synchronized (arrayList) {
if(arrayList.size() == 0){
try {
arrayList.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
arrayList.remove(0);
}
}
}
public void run(){
System.out.println("Thread name -- "+this.getName());
if(!this.getName().equalsIgnoreCase("p4")){
this.removeElement();
}
this.addElement("threads");
}
public static void main(String[] args) {
PrinterResource p1 = new PrinterResource();
p1.setName("p1");
p1.start();
PrinterResource p2 = new PrinterResource();
p2.setName("p2");
p2.start();
PrinterResource p3 = new PrinterResource();
p3.setName("p3");
p3.start();
PrinterResource p4 = new PrinterResource();
p4.setName("p4");
p4.start();
try{
p1.join();
p2.join();
p3.join();
p4.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Final size of arraylist "+arrayList.size());
}
}