可以多次通知唤醒同一个线程吗?

Fra*_*fka 9 java multithreading synchronization notify

想象一下,你有一个典型的Java生产者 - 消费者模式.要想要使用更高效,notify()而不是notifyAll()在将新元素添加到队列时.如果两个生产者线程调用notify,是否可以保证唤醒两个不同的等待消费者线程?或者可能是两个notify()在彼此之后不久被解雇导致同一个comsumer线程排队等待两次唤醒?我找不到该部分是描述这是如何工作的API.java是否有一些原子内部操作来唤醒线程一次?

如果只有一个消费者在等待,那么第二个通知将会丢失,这没有问题.

Gra*_*ray 13

我的答案有一些具体的实施信息.它基于我对Sun JVM和其他线程库行为的工作知识.

如果两个生产者线程调用notify,是否可以保证唤醒两个不同的等待消费者线程?

不它不是.无法保证会有任何消费者醒来.保证的是,如果有2个线程在等待,那么将有2个不同的线程放入运行队列.

或者可能是两个notify()在彼此之后不久被解雇导致同一个comsumer线程排队等待两次唤醒?

notify()不会.两次调用不会导致相同的消费者线程排队两次.但是,它可能导致一个线程被唤醒,并且可能没有其他线程在等待,因此第二个notify()调用可能什么都不做.当然线程可能已经被唤醒然后再次等待,所以第二次notify()调用就这样,但我不认为这就是你所要求的.

java是否有一些原子内部操作来唤醒线程一次?

是.该Thread代码具有许多同步点.一旦线程被通知,它就会被移出wait队列.未来的调用notify()将调查wait队列而不是找到线程.

重要的一点.对于生产者/消费者模型,始终确保您在while循环中测试条件.原因是有消费者的竞争条件被锁定但没有等待条件.

 synchronized (workQueue) {
     // you must do a while here
     while (workQueue.isEmpty()) {
         workQueue.wait();
     }
     workQueue.remove();
 }
Run Code Online (Sandbox Code Playgroud)

Consumer1可能在等待workQueue. Consumer2可能会被阻止synchronized但在运行队列中.如果事情被放入workQueueworkQueue.notify()被调用. Consumer2现已投入运行队列,但背后 Consumer1谁在那里第一次.这是一种常见的实现方式.所以,Consumer1去在将删除的项目workQueueConsumer2被通报的. Consumer2必须再次测试是否workQueue为空,否则remove()将抛出,因为队列再次为空.有关比赛的更多详情,请参见此处.

同样重要的是要意识到已经记录了虚假的while唤醒,因此循环可以防止线程在没有wait()调用的情况下被唤醒.

所有这些说,如果您可以通过使用BlockingQueue其他答案中建议的减少您的生产者/消费者代码,那么您应该这样做.该BlockingQueue代码已经解决了所有这些问题.