Lock sharedLock = new ReentrantLock();
Condition condition = lock.newCondition();
主线程:
sharedLock.lock();
childThread.start();
condition.await(5, TimeUnit.SECONDS);
sharedLock.unlock();
子线程:
sharedLock.lock();
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
condition.signal();
sharedLock.unlock();
假设子线程发送网络请求并等待响应,我希望主线程最多等待5秒,如果超时,则重试请求。但是当await()
超时时,它无法获取锁,因为子线程仍然保持它,所以它仍然等待锁,直到子线程释放它,这需要10秒。
如何实现我的要求主线程等待子线程的信号,但是有一个有限的超时?
答案 0 :(得分:1)
这不是你应该怎么做的,你应该:
ExecutorService
(线程池),您应该检查类Executors
的方法以选择最适合您的方法,但Executors.newFixedThreadPool
是一个好的开始FutureTask
提交给线程池get
TimeoutException
以下是如何做到的:
// Total tries
int tries = 3;
// Current total of tries
int tryCount = 1;
do {
// My fake task to execute asynchronously
FutureTask<Void> task = new FutureTask<>(
() -> {
Thread.sleep(2000);
return null;
}
);
// Submit the task to the thread pool
executor.submit(task);
try {
// Wait for a result during at most 1 second
task.get(1, TimeUnit.SECONDS);
// I could get the result so I break the loop
break;
} catch (TimeoutException e) {
// The timeout has been reached
if (tryCount++ == tries) {
// Already tried the max allowed so we throw an exception
throw new RuntimeException(
String.format("Could execute the task after %d tries", tries),
e
);
}
}
} while (true);
如何实现主线程等待子线程的要求 信号,但有一个有限的超时?
以下是如何达到您的要求:
主线程:
lock.lock();
try {
childThread.start();
condition.await(5, TimeUnit.SECONDS);
} finally {
sharedLock.lock();
}
子线程:
try {
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
} finally {
// Here we notify the main thread that the task is complete whatever
// the task failed or not
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
正如您所看到的那样,任务不能在关键部分内执行,我们只获取锁定以通知主线程。否则,如果您在超时后执行临界区内的任务,主线程仍然需要再次获取锁,并且由于锁实际上是由子线程拥有的,因此无论如何都需要等待,直到任务结束为止。使超时毫无用处。
NB:我将sharedLock
重命名为lock
,因为ReentrantLock
是一个独占锁而不是共享锁,如果您需要共享锁检查该类Semaphore
来定义许可总额。
答案 1 :(得分:1)
您可以使用intrinsic lock简化代码。
Object sharedObj = new Object();
主线程:
synchronized (sharedObj) {
int retryCount = 0;
while (retryCount < maxRetry) {
sharedObj.wait(5000);
retryCount++;
}
}
子线程:
synchronized (sharedObj) {
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
sharedObj.notify();
}
java condition await timeout但无法返回
那是因为必须释放锁定所以 wait / await 可以返回。所以你的子线程应该是:
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
synchronized (sharedObj) {
sharedObj.notify();
}
Java的 wait / notify 通常用于解决生产者 - 消费者问题。 通常 sharedObj 不应该持有太长时间。然后当等待超时时,主线程可以再次保持锁定。
查看生产中的示例:hadoop/hdfs/DFSOutputStream.java 逻辑很简单,生产者创建数据包并将其放入dataQueue
// takes a long time to create packet
synchronized (dataQueue) {
dataQueue.addLast(packet);
dataQueue.notifyAll();
}
消费者在dataQueue为空时等待:
synchronized (dataQueue) {
while ((!shouldStop() && dataQueue.size() == 0 &&... ) {
try {
dataQueue.wait(timeout);
} catch (InterruptedException e) {
LOG.warn("Caught exception", e);
}
doSleep = false;
now = Time.monotonicNow();
}
如您所见, dataQueue 大部分时间都已解锁!
如何实现我的要求主线程等待子线程的信号,但是有一个有限的超时?
如果您的子线程主要位于循环中,则主线程可以设置 isRunning 标志以使子线程自行停止。如果您的子线程主要通过I / O操作阻塞,则主线程可以中断子线程。
sharedObj用于协调并保护 sharedObj 。如果有其他资源需要保护,您有两个选择:
1.如果资源上的操作很快,例如 DFSOutputStream.java 中的 ackQueue ,请在 sharedObj 中一起保护它。
2.如果对资源的操作非常耗时,请执行此操作并在 sharedObj 之外保护它。
答案 2 :(得分:0)
问题中的有效混淆是因为“Thread.sleep(10)”是在锁定块内完成的。当等待(长时间,TimeUnit单元)由于超时而必须返回时,它仍然需要锁定。因此,正如在另一个答案中所建议的那样,长时间运行的任务不应该在锁内部才能正常工作。 但是,如果有适当的文件强调这一事实,那就太好了。例如,如果我们等待(5,TimeUnit.SECONDS)即等待5秒并且在调用后10秒锁定可用,即使锁定在返回时现在可用,它仍将返回false。