多线程:java 条件等待超时但不能 return
multithread: java condition await timeout but can't return
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秒。
如何实现主线程等待子线程信号但有一个有限超时的要求?
这不是你应该做的,你应该:
- 为此创建一个
ExecutorService
(线程池),您应该检查 class 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);
How can I achieve my requirement that main thread wait child thread's
signal, but have a bounded timeout?
您可以通过以下方式实现您的要求:
主线程:
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();
}
}
正如你所看到的工作,任务一定不能在临界区内执行,我们只是获取锁通知主线程而已。否则,如果超时后在临界区内执行任务,主线程仍将需要再次获取锁,并且由于锁实际上由子线程拥有,因此无论如何它都需要等待直到任务结束使超时完全无用。
注意: 我将 sharedLock
重命名为 lock
因为 ReentrantLock
是排他锁而不是共享锁,如果你需要共享锁检查 class Semaphore
来定义许可总量。
您的代码可以使用 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 but can't return
那是因为必须释放锁才能wait/await才能return。所以你的子线程应该是这样的:
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
synchronized (sharedObj) {
sharedObj.notify();
}
Java的wait/notify通常用来解决生产者-消费者问题。 通常 sharedObj 不应该持有太久。然后你的主线程可以在 wait 超时时再次持有锁。
看一个生产中的例子:hadoop/hdfs/DFSOutputStream.java
逻辑很简单,producer创建packet放到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 大部分时间都处于解锁状态!
How can I achieve my requirement that main thread wait child thread's signal, but have a bounded timeout?
如果您的子线程大部分处于循环中,您的主线程可以设置一个 isRunning 标志来让子线程自行停止。如果您的子线程主要被 I/O 操作阻塞,您的主线程可以中断子线程。
sharedObj用于协调和保护sharedObj。如果还有其他资源需要保护,你有2个选择:
1.如果对资源的操作比较快,比如DFSOutputStream.java中的ackQueue,一起保护在里面sharedObj.
2.如果对资源的操作比较耗时,在sharedObj.
之外进行保护
问题中的有效混淆是因为 "Thread.sleep(10)" 是在锁块内完成的。当 await(long time, TimeUnit unit) 由于超时而不得不 return 时,它仍然需要锁。因此,正如另一个答案中所建议的那样,长 运行 任务不应该在锁内才能正常工作。
但是最好有适当的文档来强调这一事实。例如,如果我们 await(5, TimeUnit.SECONDS) 即等待 5 秒并且锁在调用后 10 秒可用,即使此时锁可用,它仍然 return false共 return.
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秒。
如何实现主线程等待子线程信号但有一个有限超时的要求?
这不是你应该做的,你应该:
- 为此创建一个
ExecutorService
(线程池),您应该检查 classExecutors
的方法以选择最适合您的方法,但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);
How can I achieve my requirement that main thread wait child thread's signal, but have a bounded timeout?
您可以通过以下方式实现您的要求:
主线程:
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();
}
}
正如你所看到的工作,任务一定不能在临界区内执行,我们只是获取锁通知主线程而已。否则,如果超时后在临界区内执行任务,主线程仍将需要再次获取锁,并且由于锁实际上由子线程拥有,因此无论如何它都需要等待直到任务结束使超时完全无用。
注意: 我将 sharedLock
重命名为 lock
因为 ReentrantLock
是排他锁而不是共享锁,如果你需要共享锁检查 class Semaphore
来定义许可总量。
您的代码可以使用 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 but can't return
那是因为必须释放锁才能wait/await才能return。所以你的子线程应该是这样的:
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
synchronized (sharedObj) {
sharedObj.notify();
}
Java的wait/notify通常用来解决生产者-消费者问题。 通常 sharedObj 不应该持有太久。然后你的主线程可以在 wait 超时时再次持有锁。
看一个生产中的例子:hadoop/hdfs/DFSOutputStream.java 逻辑很简单,producer创建packet放到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 大部分时间都处于解锁状态!
How can I achieve my requirement that main thread wait child thread's signal, but have a bounded timeout?
如果您的子线程大部分处于循环中,您的主线程可以设置一个 isRunning 标志来让子线程自行停止。如果您的子线程主要被 I/O 操作阻塞,您的主线程可以中断子线程。
sharedObj用于协调和保护sharedObj。如果还有其他资源需要保护,你有2个选择:
1.如果对资源的操作比较快,比如DFSOutputStream.java中的ackQueue,一起保护在里面sharedObj.
2.如果对资源的操作比较耗时,在sharedObj.
问题中的有效混淆是因为 "Thread.sleep(10)" 是在锁块内完成的。当 await(long time, TimeUnit unit) 由于超时而不得不 return 时,它仍然需要锁。因此,正如另一个答案中所建议的那样,长 运行 任务不应该在锁内才能正常工作。 但是最好有适当的文档来强调这一事实。例如,如果我们 await(5, TimeUnit.SECONDS) 即等待 5 秒并且锁在调用后 10 秒可用,即使此时锁可用,它仍然 return false共 return.