我有一个无限期运行的工作线程,如果没有任何关系,它会进入睡眠状态一分钟。有时,另一段代码会产生一些工作,并希望立即唤醒工作线程。
所以我做了类似的事情(仅用于说明的代码):
class Worker {
public void run() {
while (!shuttingDown()) {
step();
}
}
private synchronized void step() {
if (hasWork()) {
doIt();
} else {
wait(60_000);
}
}
public synchronized wakeMeUpInside() {
notify();
}
}
我不喜欢进入显示器只是为了唤醒某些东西,这意味着通知线程可能会被推迟没有充分理由。由于原生同步的选择有限,我想我会切换到Condition
,但它有exactly the same problem:
当调用此方法时,实现可能(通常会)要求当前线程保持与此Condition关联的锁。
答案 0 :(得分:2)
这是一个基于信号量的解决方案:
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
两次而不会阻塞。您可以扩展示例以避免这种情况。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);
}
}