多线程:Java的状态等待超时,但不能返回
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
(线程池)为您应检查类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();
}
}
正如你所看到的工作,任务必须不在关键部分内执行,我们只获取锁定以通知主线程。否则,如果在超时之后在临界区内执行任务,主线程仍需要再次获取锁,并且由于锁实际上由子线程拥有,所以它将需要等待直到任务结束使超时完全无用。
注:我改名sharedLock
到lock
为ReentrantLock
是一个排他锁不是共享锁,如果你需要一个共享锁检查类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的状态等待超时,但不能返回
这是因为锁必须被释放所以等待/等待才能返回。所以,你的子线程应该是这样的:
//do something, may take a long time
Thread.sleep(10);// sleep to simulate a long execution
synchronized (sharedObj) {
sharedObj.notify();
}
Java的等待/通知通常是用来解决生产者 - 消费者问题。 而且通常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。如果有其它资源应该得到保护,你有2种选择:
1.如果资源上的操作快捷,像ackQueue在DFSOutputStream.java,一起保护它sharedObj内。
2.如果资源上的操作比较耗时,请在sharedObj以外的地方进行操作。
您是否需要使用'Lock'和'Condition'(如在作业中)?有比简单的解决方案,而不是建立这样一个大多数的锁定和条件的尝试机制。 – zapl
你的sharedLock的目的是什么?你想用它来防止什么? –
@NicolasFilotto我想使用信号并等待。 condition.await和condition.signal必须与锁一起使用,或者不能调用await和signal方法。 – Moon