我有一个无限期运行的工作线程,如果没有什么可做的,它就会 hibernate 一分钟。有时,另一段代码会产生一些工作并希望立即唤醒工作线程。
所以我做了这样的事情(代码仅供说明):
class Worker {
public void run() {
while (!shuttingDown()) {
step();
}
}
private synchronized void step() {
if (hasWork()) {
doIt();
} else {
wait(60_000);
}
}
public synchronized wakeMeUpInside() {
notify();
}
}
我不喜欢的是必须进入监视器才能唤醒某些东西,这意味着通知线程可能会无缘无故地延迟。由于 native 同步的选择有限,我想我应该切换到 Condition
,但它有 exactly the same problem :
An implementation may (and typically does) require that the current thread hold the lock associated with this Condition when this method is called.
最佳答案
这是一个基于信号量的解决方案:
class Worker {
// If 0 there's no work available
private workAvailableSem = new Semaphore(0);
public void run() {
while (!shuttingDown()) {
step();
}
}
private synchronized void step() {
// Try to obtain a permit waiting up to 60 seconds to get one
boolean hasWork = workAvailableSem.tryAquire(1, TimeUnit.MINUTES);
if (hasWork) {
doIt();
}
}
public wakeMeUpInside() {
workAvailableSem.release(1);
}
}
我不能 100% 确定这能满足您的需求。有几点需要注意:
- 这将每次添加一个许可证
wakeMeUpInside
叫做。因此,如果两个线程唤醒Worker
它将运行doIt
两次无阻塞。您可以扩展该示例来避免这种情况。 - 这会等待 60 秒才能完成工作。如果没有可用的,它最终会回到
run
方法,它将立即将其发送回step
方法将再次等待。我这样做是因为我假设您有某种原因想要每 60 秒运行一次,即使没有工作。如果不是这种情况,请调用aquire
而且你会无限期地等待工作。
根据下面的评论,OP 只想运行一次。虽然您可以调用 drainPermits
在这种情况下,更干净的解决方案就是使用 LockSupport
像这样:
class Worker {
// We need a reference to the thread to wake it
private Thread workerThread = null;
// Is there work available
AtomicBoolean workAvailable = new AtomicBoolean(false);
public void run() {
workerThread = Thread.currentThread();
while (!shuttingDown()) {
step();
}
}
private synchronized void step() {
// Wait until work is available or 60 seconds have passed
ThreadSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
if (workAvailable.getAndSet(false)) {
doIt();
}
}
public wakeMeUpInside() {
// NOTE: potential race here depending on desired semantics.
// For example, if doIt() will do all work we don't want to
// set workAvailable to true if the doIt loop is running.
// There are ways to work around this but the desired
// semantics need to be specified.
workAvailable.set(true);
ThreadSupport.unpark(workerThread);
}
}
关于java - 唤醒线程而不会有被阻塞的风险,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42322324/