Can notify wake up the same thread multiple times?

 ̄綄美尐妖づ 提交于 2019-12-30 08:18:09

问题


Imagine you have a typical producer-consumer pattern in Java. To be a bit more efficient you want to use notify() and not notifyAll() when a new element is added to the queue. If two producer threads invoke notify, is it guaranteed that two distinct waiting consumer threads will be awoken? Or can it be that two notify()s fired shortly after each other cause the same comsumer thread to be queued for wakeup twice? I can't find the section is the API describing how this exactly works. Does java have some atomic internal operation for waking up threads exactly once?

If only one comsumer is waiting then the second notify would be lost, that is no problem.


回答1:


My answer has some implementation specific information. It is based on my working knowledge of Sun JVM and other thread library behavior.

If two producer threads invoke notify, is it guaranteed that two distinct waiting consumer threads will be awoken?

No it is not. There is no guarantee that there will be any consumers awoken. What is guaranteed is that if there are 2 threads that are waiting, then 2 different threads will be put into the run queue.

Or can it be that two notify()s fired shortly after each other cause the same comsumer thread to be queued for wakeup twice?

No. Two notify() calls will not cause the same consumer thread to be queued twice. It may however, cause one thread to be awoken and there may not be other threads waiting, so the second notify() call may do nothing. Of course the thread could have been awoken and then gone right back waiting again and so get the second notify() call that way, but I don't think think that is what you are asking.

Does java have some atomic internal operation for waking up threads exactly once?

Yes. The Thread code has a number of synchronization points. Once a thread has been notified it is moved out of the wait queue. Future calls to notify() will look into the wait queue and not find the thread.

One more important point. With producer/consumer models, always make sure you are testing the condition in a while loop. The reason is that there is race conditions with consumers that are blocked on the lock but not waiting on the condition.

 synchronized (workQueue) {
     // you must do a while here
     while (workQueue.isEmpty()) {
         workQueue.wait();
     }
     workQueue.remove();
 }

Consumer1 could be waiting on workQueue. Consumer2 could be blocked at the synchronized but in the run-queue. If something is put into the workQueue and workQueue.notify() is called. Consumer2 is put into run-queue now but is behind Consumer1 who is there first. This is a common implementation. So Consumer1 goes in a removes the item from the workQueue that Consumer2 was notified about. Consumer2 has to test again if the workQueue is empty otherwise remove() will throw because the queue is empty again. See here for more details of the race.

It is also important to realize that spurious wakeups have been documented so the while loop protects against a thread being awoken without a wait() call.

All this said, if you can reduce your producer/consumer code by using a BlockingQueue as recommended in other answers then you should do so. The BlockingQueue code already has solved all of these issues.




回答2:


Yes, What you describe can happen.

As described in the javadoc, notify wakes an arbitrary thread. So if your thread is finished and has called wait before the next notify, then it is one of the arbitrary candidates for waking.

I have extensive experience with multithreaded applications, and I find that I use one of these two patterns:

  1. There are multiple sleeping threads which need to wake up on an event, and the order in which they wake doesn't matter. In this case, I use notifyAll to wake them.

  2. There is one sleeping thread which needs to wake up on an event. In this case, I use notify to wake it.

If I ever have a situation where there are multiple sleeping threads and I want to only wake one of them, I use a different design to achieve this. Basically, I build something out myself so that there is no arbitrary decision being made by the runtime environment. I always want to know exactly what will wake.

I break down a design into one of these two scenarios, or I use something from the java.util.concurrent package. I never have problems with spurious notifications, but I am also very careful on the objects I use for locking. I tend to create vanilla Object instances whose only purpose is to be the target of a lock operation, but occasionally I'll use an object whose class type is well-defined and under my control.




回答3:


From the javadoc for notify():

The choice of which thread to notify "is arbitrary and occurs at the discretion of the implementation"

It will almost certainly not be a "fair" (as the term is used in computer science and parallelism) algorithm for waking threads. It's entirely possible that the same thread is woken up twice in quick succession. Also note that spurious notifications are also possible.

In general, I agree with the comments that suggest using a BlockingQueue implementation instead of doing this yourself.




回答4:


You can use ReentrantLock to obtain a fair arrival-order policy. The interface ReadWriteLock, you obtain the producer-consumer behavior. The class ReentrantReadWriteLock combine both capabilities.

Or

You should use ArrayBlockingQueue, where this pattern is already implemented.

int capacity = 10;
boolean fair = true;
new ArrayBlockingQueue(capacity, fair);


来源:https://stackoverflow.com/questions/10684111/can-notify-wake-up-the-same-thread-multiple-times

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!