這裏是一個信號量的解決方案:
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);
}
}
我想,我做的事情是傻瓜......其實,只有'wait'和'notify'需要被包含在同步塊中。還有另一個無關的同步,我混淆了...... – maaartinus
我建議使用'BlockingQueue'。這樣你就可以忘記'synchronized'和'wait/notify'。當你關閉時,你只需要中斷'queue.poll()'等待的線程。 – Kayaman
@Kayaman我知道'BlockingQueue',但我沒有把工作發送到線程,我只是通知它。而且,所有工作(由多個線程產生)可能或可能不會在一個步驟中執行。 – maaartinus