是否存在“阻塞直到条件变为真”? java中的函数?
我正在为服务器编写一个侦听器线程,目前我正在使用:
while (true){
try {
if (condition){
//do something
condition=false;
}
sleep(1000);
} catch (InterruptedException ex){
Logger.getLogger(server.class.getName()).log(Level.SEVERE, null, ex);
}
}
通过上面的代码,我遇到了 run 函数占用所有 cpu 时间循环的问题。睡眠功能可以工作,但这似乎只是权宜之计,而不是解决方案。
是否有一些函数会阻塞直到变量“条件”变为“真”? 或者连续循环是等待变量值更改的标准方法?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
像这样的轮询绝对是最不受欢迎的解决方案。
我假设您有另一个线程将执行某些操作以使条件成立。有多种方法可以同步线程。在您的情况下,最简单的一个是通过对象发出通知:
主线程:
其他线程:
syncObject
本身可以是一个简单的对象
。线程间通信还有许多其他方式,但使用哪一种取决于您正在做什么。
Polling like this is definitely the least preferred solution.
I assume that you have another thread that will do something to make the condition true. There are several ways to synchronize threads. The easiest one in your case would be a notification via an Object:
Main thread:
Other thread:
syncObject
itself can be a simpleObject
.There are many other ways of inter-thread communication, but which one to use depends on what precisely you're doing.
EboMike 的回答和Toby 的回答< /a> 都在正确的轨道上,但它们都包含一个致命的缺陷。该缺陷称为丢失通知。
问题是,如果一个线程调用 foo.notify(),它根本不会执行任何操作,除非其他线程已经在 foo.wait() 调用中休眠。对象
foo
不记得它已被通知。除非线程在 foo 上同步,否则不允许调用
foo.wait()
或foo.notify()
是有原因的。这是因为避免丢失通知的唯一方法是使用互斥锁来保护条件。如果做得正确,它看起来像这样:消费者线程:
生产者线程:
更改条件的代码和检查条件的代码都在同一个对象上同步,并且消费者线程在等待之前显式测试条件。当条件已经成立时,消费者不可能错过通知并最终永远陷入
wait()
调用中。另请注意
wait()
处于循环中。这是因为,在一般情况下,当消费者重新获取 foo 锁并唤醒时,其他某个线程可能再次使条件为 false。即使这在你的程序中是不可能的,但在某些操作系统中,即使foo.notify( )
尚未被调用。这称为“虚假唤醒”,它被允许发生,因为它使等待/通知更容易在某些操作系统上实现。EboMike's answer and Toby's answer are both on the right track, but they both contain a fatal flaw. The flaw is called lost notification.
The problem is, if a thread calls
foo.notify()
, it will not do anything at all unless some other thread is already sleeping in afoo.wait()
call. The object,foo
, does not remember that it was notified.There's a reason why you aren't allowed to call
foo.wait()
orfoo.notify()
unless the thread is synchronized on foo. It's because the only way to avoid lost notification is to protect the condition with a mutex. When it's done right, it looks like this:Consumer thread:
Producer thread:
The code that changes the condition and the code that checks the condition is all synchronized on the same object, and the consumer thread explicitly tests the condition before it waits. There is no way for the consumer to miss the notification and end up stuck forever in a
wait()
call when the condition is already true.Also note that the
wait()
is in a loop. That's because, in the general case, by the time the consumer re-acquires thefoo
lock and wakes up, some other thread might have made the condition false again. Even if that's not possible in your program, what is possible, in some operating systems, is forfoo.wait()
to return even whenfoo.notify()
has not been called. That's called a spurious wakeup, and it is allowed to happen because it makes wait/notify easier to implement on certain operating systems.由于没有人发布 CountDownLatch 的解决方案。怎么样:
As nobody published a solution with CountDownLatch. What about:
与 EboMike 的答案类似,您可以使用类似于 wait/notify/notifyAll 的机制,但准备使用
Lock
。例如,
您将等待另一个线程通知的某些条件(在本例中调用
doSomethingElse
),此时,第一个线程将继续...使用
Lock< /code> 相对于内在同步有很多优点,但我只是更喜欢有一个显式的
Condition
对象来表示条件(您可以有多个对象,这对于生产者-消费者之类的事情来说是一种很好的接触) 。另外,我忍不住注意到您如何处理示例中的中断异常。您可能不应该像这样使用异常,而是使用 Thread.currentThread().interrupt 重置中断状态标志。
这是因为如果抛出异常,中断状态标志将被重置(它表示“我不再记得被打断过,如果他们询问,我将无法告诉其他人我曾经被打断过”) >"),另一个过程可能依赖于这个问题。例子是其他东西已经基于此实施了中断策略......唷。另一个例子可能是您的中断策略,而不是
while(true)
可能已实现为while(!Thread.currentThread().isInterrupted()
(这将也让你的代码更加......社会考虑)所以,总而言之,当你想使用
Lock
时,使用Condition
大致相当于使用 wait/notify/notifyAll >,日志记录是邪恶的,吞下InterruptedException
是顽皮的;)Similar to EboMike's answer you can use a mechanism similar to wait/notify/notifyAll but geared up for using a
Lock
.For example,
Where you'll wait for some condition which is notified by another thread (in this case calling
doSomethingElse
), at that point, the first thread will continue...Using
Lock
s over intrinsic synchronisation has lots of advantages but I just prefer having an explicitCondition
object to represent the condition (you can have more than one which is a nice touch for things like producer-consumer).Also, I can't help but notice how you deal with the interrupted exception in your example. You probably shouldn't consume the exception like this, instead reset the interrupt status flag using
Thread.currentThread().interrupt
.This because if the exception is thrown, the interrupt status flag will have been reset (it's saying "I no longer remember being interrupted, I won't be able to tell anyone else that I have been if they ask") and another process may rely on this question. The example being that something else has implemented an interruption policy based on this... phew. A further example might be that your interruption policy, rather that
while(true)
might have been implemented aswhile(!Thread.currentThread().isInterrupted()
(which will also make your code be more... socially considerate).So, in summary, using
Condition
is rougly equivalent to using wait/notify/notifyAll when you want to use aLock
, logging is evil and swallowingInterruptedException
is naughty ;)您可以使用 信号量 。
当条件不满足时,另一个线程获取信号量。
您的线程将尝试使用
acquireUninterruptically()
获取它
或
tryAcquire(int Permits, long timeout, TimeUnit unit)
并将被阻止。当条件满足时,信号量也会被释放,并且您的线程将获取它。
您还可以尝试使用
SynchronousQueue< /code>
或
CountDownLatch
。You could use a semaphore.
While the condition is not met, another thread acquires the semaphore.
Your thread would try to acquire it with
acquireUninterruptibly()
or
tryAcquire(int permits, long timeout, TimeUnit unit)
and would be blocked.When the condition is met, the semaphore is also released and your thread would acquire it.
You could also try using a
SynchronousQueue
or aCountDownLatch
.人们还可以利用 CompletableFuture(自 Java 8 起):
One could also leverage
CompletableFuture
s (since Java 8):无锁解决方案(?)
我有同样的问题,但我想要一个不使用锁的解决方案。
问题:我最多有一个线程从队列中消耗。多个生产者线程不断地向队列中插入数据,并且需要通知消费者是否正在等待。队列是无锁的,因此使用锁进行通知会导致生产者线程不必要的阻塞。每个生产者线程都需要先获取锁,然后才能通知等待的消费者。我相信我使用
LockSupport
和AtomicReferenceFieldUpdater
。如果 JDK 中存在无锁屏障,我找不到它。据我所知,CyclicBarrier
和CoundDownLatch
都在内部使用锁。这是我稍微缩写的代码。需要明确的是,这段代码一次只允许一个线程等待。可以修改它以允许多个等待者/消费者,方法是使用某种类型的原子集合来存储多个所有者(
ConcurrentMap
可能有效)。我已经使用了这段代码,它似乎有效。我还没有对其进行广泛的测试。我建议您阅读
LockSupport
。为了给出一个模糊的用法示例,我将采用 jameslarge 的示例:
消费者线程(单数,不是复数!):
生产者线程:
Lock-free solution(?)
I had the same issue, but I wanted a solution that didn't use locks.
Problem: I have at most one thread consuming from a queue. Multiple producer threads are constantly inserting into the queue and need to notify the consumer if it's waiting. The queue is lock-free so using locks for notification causes unnecessary blocking in producer threads. Each producer thread needs to acquire the lock before it can notify the waiting consumer. I believe I came up with a lock-free solution using
LockSupport
andAtomicReferenceFieldUpdater
. If a lock-free barrier exists within the JDK, I couldn't find it. BothCyclicBarrier
andCoundDownLatch
use locks internally from what I could find.This is my slightly abbreviated code. Just to be clear, this code will only allow one thread to wait at a time. It could be modified to allow for multiple awaiters/consumers by using some type of atomic collection to store multiple owner (a
ConcurrentMap
may work).I have used this code and it seems to work. I have not tested it extensively. I suggest you read the documentation for
LockSupport
before use.To give a vague example of usage, I'll adopt james large's example:
Consumer thread (singular, not plural!):
Producer thread(s):