Java 即使任务已完成,future.isDone也返回false
我遇到了棘手的情况,DoesJava 即使任务已完成,future.isDone也返回false,java,multithreading,future,Java,Multithreading,Future,我遇到了棘手的情况,Doesfuture.isDone()返回false,即使线程已经完成 import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownL
future.isDone()
返回false
,即使线程已经完成
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class DataAccessor {
private static ThreadPoolExecutor executor;
private int timeout = 100000;
static {
executor = new ThreadPoolExecutor(10, 10, 1000, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000));
}
public static void main(String[] args) {
List<String> requests = new ArrayList<String>();
for(int i=0; i<20; i++){
requests.add("request:"+i);
}
DataAccessor dataAccessor = new DataAccessor();
List<ProcessedResponse> results = dataAccessor.getDataFromService(requests);
for(ProcessedResponse response:results){
System.out.println("response"+response.toString()+"\n");
}
executor.shutdown();
}
public List<ProcessedResponse> getDataFromService(List<String> requests) {
final CountDownLatch latch = new CountDownLatch(requests.size());
List<SubmittedJob> submittedJobs = new ArrayList<SubmittedJob>(requests.size());
for (String request : requests) {
Future<ProcessedResponse> future = executor.submit(new GetAndProcessResponse(request, latch));
submittedJobs.add(new SubmittedJob(future, request));
}
try {
if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
// some of the jobs not done
System.out.println("some jobs not done");
}
} catch (InterruptedException e1) {
// take care, or cleanup
for (SubmittedJob job : submittedJobs) {
job.getFuture().cancel(true);
}
}
List<ProcessedResponse> results = new LinkedList<DataAccessor.ProcessedResponse>();
for (SubmittedJob job : submittedJobs) {
try {
// before doing a get you may check if it is done
if (!job.getFuture().isDone()) {
// cancel job and continue with others
job.getFuture().cancel(true);
continue;
}
ProcessedResponse response = job.getFuture().get();
results.add(response);
} catch (ExecutionException cause) {
// exceptions occurred during execution, in any
} catch (InterruptedException e) {
// take care
}
}
return results;
}
private class SubmittedJob {
final String request;
final Future<ProcessedResponse> future;
public Future<ProcessedResponse> getFuture() {
return future;
}
public String getRequest() {
return request;
}
SubmittedJob(final Future<ProcessedResponse> job, final String request) {
this.future = job;
this.request = request;
}
}
private class ProcessedResponse {
private final String request;
private final String response;
ProcessedResponse(final String request, final String response) {
this.request = request;
this.response = response;
}
public String getRequest() {
return request;
}
public String getResponse() {
return response;
}
public String toString(){
return "[request:"+request+","+"response:"+ response+"]";
}
}
private class GetAndProcessResponse implements Callable<ProcessedResponse> {
private final String request;
private final CountDownLatch countDownLatch;
GetAndProcessResponse(final String request, final CountDownLatch countDownLatch) {
this.request = request;
this.countDownLatch = countDownLatch;
}
public ProcessedResponse call() {
try {
return getAndProcessResponse(this.request);
} finally {
countDownLatch.countDown();
}
}
private ProcessedResponse getAndProcessResponse(final String request) {
// do the service call
// ........
if("request:16".equals(request)){
throw (new RuntimeException("runtime"));
}
return (new ProcessedResponse(request, "response.of." + request));
}
}
}
import java.util.ArrayList;
导入java.util.LinkedList;
导入java.util.List;
导入java.util.concurrent.ArrayBlockingQueue;
导入java.util.concurrent.Callable;
导入java.util.concurrent.CountDownLatch;
导入java.util.concurrent.ExecutionException;
导入java.util.concurrent.Future;
导入java.util.concurrent.ThreadPoolExecutor;
导入java.util.concurrent.TimeUnit;
公共类数据存取器{
私有静态线程池执行器;
私有int超时=100000;
静止的{
executor=新的ThreadPoolExecutor(10,10,1000,TimeUnit.SECONDS,新的ArrayBlockingQueue(1000));
}
公共静态void main(字符串[]args){
列表请求=新建ArrayList();
对于(int i=0;i问题最有可能是计时问题。闩锁将在所有任务实际完成之前释放(关于未来)(因为countDown()
调用在call()
方法中)
您基本上是在重新创建(实现是)的工作,我建议您改用它
方法获得结果。只需跟踪总时间,并确保将每次调用的超时时间减少到总剩余时间。正如jtahlborn所提到的,这可能是一种竞赛条件,其中倒计时闩锁向其等待线程发出信号,等待线程在Fu之前评估未来的取消条件tureTask完成其执行(将在倒计时后的某个时间点执行)
你不能仅仅依靠CountdownLatch的同步机制与未来的同步机制保持同步。你应该做的是依靠未来告诉你何时完成
您可以Future.get(长超时,TimeUnit.ms)
而不是CountdownLatch.wait(长超时,TimeUnit.ms)
。要获得与闩锁相同类型的效果,您可以将所有的未来
添加到列表中
,在列表上迭代并获得每个未来。以下是竞争条件的场景:
- 主线程位于
锁存器中。等待,它在毫秒内不会从Java调度程序接收CPU插槽
- 最后一个执行器线程在
finally
子句中调用countDownLatch.countDown()
- Java调度程序决定给予主线程更多的优先级,因为它等待了一段时间
- 因此,当它请求最后一个
未来
结果时,它还不可用,因为最后一个执行器线程没有时间片来传播结果,它仍然处于最终
我还没有找到关于Java调度器如何工作的详细解释,可能是因为它主要取决于运行JVM的操作系统,但一般来说,它试图平均在一段时间内将CPU分配给可运行线程。这就是为什么主线程可以先到达isDone
测试一个人留下了finally
子句
我建议您在锁存之后更改结果“collect”。等待。正如您所知,锁存已减为零(主线程中断时除外),所有结果都应该很快可用。带有timeout的get方法让调度程序有机会将时间片分配给仍在finally子句中等待的最后一个线程:
for (SubmittedJob job : submittedJobs) {
try {
ProcessedResponse response = null;
try {
// Try to get answer in short timeout, should be available
response = job.getFuture().get(10, TimeUnit.MILLISECONDS);
} catch (TimeoutException te) {
job.getFuture().cancel(true);
continue;
}
results.add(response);
} catch (ExecutionException cause) {
// exceptions occurred during execution, in any
} catch (InterruptedException e) {
// take care
}
}
备注:您的代码不现实,因为getAndProcessResponse
方法在不到一毫秒的时间内就结束了。在随机睡眠的情况下,竞争条件不会经常出现。我赞同关于竞争条件的观点。
我建议忘记门闩,使用它
java.util.concurrent.ThreadPoolExecutor.awaitTermination(长,时间单位)
您的代码没有意义。pooledExecutor.submit(可调用)
不起作用,在哪里实例化可调用的
?在哪里创建闩锁,在哪里等待它?我的怀疑是,可调用的可能会抛出一个未捕获的异常,从而中止工作线程,但是在退出调用
@PéterTörök之前也会倒计时闩锁,而这不起作用遇到ExecutionException?您能解释一下吗?只有在调用Future.get()时,才会得到ExecutionException。我会使用for(Future Future:futures)Future.get();
而不是使用倒计时锁存器或检查isDone()@yadab,我也检查了Javadoc,它确实表明我的怀疑是错误的。我仍然建议在使用线程时以某种方式处理异常。我应该在哪里调用countDown()然后?倒计时处于正确的位置,但您不能依赖倒计时闩锁与未来同步。它们有各自独立的同步控件。也就是说,倒计时闩锁可能已到达所有参与方,但未来任务尚未发出完成的信号。您需要一个或另一个。@JohnVint so,而不是check ing future.IsDone,我会直接打电话给future.get(dummyTimeout)?或者有其他好办法吗?你能用另一个答案解释吗?@yadab我当然会。但这个答案确实回答了你的问题,以防你打算接受。