如何在特定时间内运行线程并在时间过去后返回一些结果?
到目前为止,我能想到的最佳解决方案是手动测量时间。但也许有更优雅,开箱即用的解决方案?
我有一个算法,在每次迭代中改进以前的解决方案。我想在预定义的时间内在单独的线程中运行此代码。经过一段时间后,应该返回最佳(最新)解决方案。
由于我想要返回解决方案,我不能只使用Future#get(long timeout, TimeUnit unit)
- 这会导致TimeoutException
。关于在“控制”线程一段时间后中断线程的情况相同 - 在这种情况下,Future
将被取消并返回null
。
我目前的解决方案如下:
计时器逻辑:
private class ExecutionTimer {
private final long executionTimeLimit;
private long startTime;
// accepts execution time limit in _miliseconds_
public ExecutionTimer(final int executionTimeLimit) {
this.executionTimeLimit = TimeUnit.MILLISECONDS.toNanos(executionTimeLimit);
}
public void start() {
this.startTime = System.nanoTime();
}
public boolean hasElapsed() {
return (System.nanoTime() - startTime) >= executionTimeLimit;
}
}
...和工作线程:
private class WorkerThread implements Callable<Double> {
private final ExecutionTimer executionTimer;
public WorkerThread(final int executionTimeLimit) {
this.executionTimer = new ExecutionTimer(executionTimeLimit);
}
@Override
public Double call() throws Exception {
executionTimer.start();
double partialSolution = 0;
while (!executionTimer.hasElapsed()) {
// let's imagine that here solution is improved ;)
partialSolution = new Random().nextDouble();
}
return partialSolution;
}
}
修改 工作线程可以无限制地工作而不会从外部中断它 - 这很好,因为算法总能改进以前的解决方案(当然,在一些大量的时间改进相对较小之后)
答案 0 :(得分:2)
您可以将中间结果存储在共享线程安全变量中(例如,在您的情况下为volatile double
) - 当您的未来超时时,您可以从该变量中检索最新的计算值。
换句话说:
future.get(...)
返回值,请使用TimeoutException
,请通过调用yourWorkerThread.getLatestValue();
来检索该值,该volatile double latestValue
将返回在每个循环时更新的partialSolution
,而不是您的本地{{1}}。< / LI>
或者,this post指向Guava库和其他解决方案(这些都归结为我的评论中讨论的2个选项)。请注意,Guava, internally使用带有超时的未来。
答案 1 :(得分:1)
我建议使用Producer-Consumer模式:
负责培养结果的算法不断产生新的更好的结果,并将它们放入共享的线程安全资源中。
对此结果感兴趣的客户端会在每个预定义的时间间隔内使用此资源,并在您的问题中设置为超时。
资源本身可以是流(例如BlockingQueue)或单个变量。
这具有很容易推理的优点,它定义了明确的界限并且非常灵活。例如:只要根本没有结果,客户端就可以阻塞,或者只要没有新的和改进的结果,它就可以阻塞。生产者消费模式的所有变化,只需调整生产者通知条件。
答案 2 :(得分:1)
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
public class Solver implements Callable<Double> {
private final AtomicBoolean running = new AtomicBoolean(true);
public void stop() {
this.running.set(true);
}
@Override
public Double call() throws Exception {
Double answer = null;
while (this.running.get()) {
answer = keepImproving();
}
// TODO Auto-generated method stub
return answer;
}
}
class Schedular extends TimerTask {
private final Solver solver;
public Schedular(Solver solver) {
this.solver = solver;
}
@Override
public void run() {
this.solver.stop();
}
}
使用以下内容
final Solver solver = new Solver();
Schedular schedular = new Schedular(solver);
final Timer timer = new Timer();
timer.schedule(schedular, 0, TimeUnit.MINUTES.toMillis(1));
ExecutorService executor = // get a executor somehow
final Future<Double> future = executor.submit(solver);
final Double answer = future.get();
System.out.println(answer);
想法是使用Timer
&amp; TimerTask
触发停止信号,以便算法停止改善答案。
答案 3 :(得分:0)
你可以使用正常的Future.get(),它会无限期地等待。