future.isDone returns false even if the task is done
我遇到了棘手的情况,即使线程完成了,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 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()+"\ "); } 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)); } } } |
如果我调用
如果您在此处发现格式不可读的视图,请访问http://tinyurl.com/7j6cvep。
该问题很可能是时机之一。
您基本上是在重新创建CompletionService的工作(实现是ExecutorCompletionService),我建议您改用它。您可以使用
正如jtahlborn所说,这可能是一个竞争条件,其中CountdownLatch发出其等待线程信号,等待线程在FutureTask完成执行之前评估Future的取消条件(这将在
您根本无法依靠CountdownLatch的同步机制与Future的同步机制进行同步。您应该做的就是依靠Future告诉您何时完成。
您可以使用
以下是比赛条件的场景:
-
主线程在
latch.await 中,它在毫秒内没有从Java调度程序接收任何CPU插槽 -
最后一个执行程序线程在
finally 子句中调用countDownLatch.countDown() - Java调度程序决定给予主线程更高的优先级,因为等待了一段时间
-
结果,当它要求最后一个
Future 结果时,它尚不可用,因为最后一个执行程序线程没有时间片来传播结果,所以它仍在finally 中。
我还没有找到有关Java调度程序实际工作方式的详细说明,可能是因为它主要取决于运行JVM的操作系统,但通常来说,它会平均尝试在一段时间内平均将CPU分配给可运行线程。这就是为什么主线程可以在另一个线程离开
我建议您在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 } } |
备注:您的代码不现实,因为
我赞成关于比赛条件的意见。
我建议忘记闩锁并使用