2013-01-09 54 views
8

我想为在线程池中执行的线程设置超时。目前,我有下面的代码:Java:为ThreadPool中的线程设置超时

ExecutorService executor = Executors.newFixedThreadPool(8); 
for(List<String> l: partition) {    
    Runnable worker = new WorkerThread(l); 
    executor.execute(worker); 
}  

executor.shutdown(); 
while (!executor.isTerminated()) { 

} 

代码只是拆分对象的大名单变成子列表并处理根单丝中的这些子表。但这不是重点。

我想给线程池中的每个单线程超时。对于只有一个池线程我发现了以下解决方案:

Future<?> future = null; 

for (List<String> l : partition) { 
    Runnable worker = new WorkerThread(l); 
    future = executor.submit(worker); 
} 

try { 
    System.out.println("Started.."); 
    System.out.println(future.get(3, TimeUnit.SECONDS)); 
    System.out.println("Finished!"); 
} catch (TimeoutException e) { 
    System.out.println("Terminated!"); 
} 

,但这不会超过一个线程工作。也许我必须把每个线程放在一个List<Future>列表中,并遍历这个列表并为每个对象设置一个超时时间?

有什么建议吗?

编辑使用CountDownLatch AFTER:

CountDownLatch doneSignal = new CountDownLatch(partition.size()); 
List<Future<?>> tasks = new ArrayList<Future<?>>(); 
ExecutorService executor = Executors.newFixedThreadPool(8); 
for (List<String> l : partition) { 
    Runnable worker = new WorkerThread(l); 
    tasks.add(executor.submit(doneSignal, worker)); 
} 

doneSignal.await(1, TimeUnit.SECONDS); 
if (doneSignal.getCount() > 0) { 
    for (Future<?> fut : tasks) { 
    if (!fut.isDone()) { 
     System.out.println("Task " + fut + " has not finshed!"); 
     //fut.cancel(true) Maybe we can interrupt a thread this way?! 
    } 
    } 
} 

工作好为止。

那么下一个问题是如何中断超时的线程?我尝试fut.cancel(true)并添加在工作线程的一些关键回路下面的结构:

if(Thread.interrupted()) { 
    System.out.println("!!Thread -> " + Thread.currentThread().getName() + " INTERRUPTED!!"); 
     return; 
} 

所以工作线程超时后“封杀”。这是一个好的解决方案吗?

此外:是否有可能通过Future接口超时线程的名称?目前,我必须在Thread.interrupted()结构的if条件中打印出名称。

感谢您的帮助!

Regards

+1

是的,它适用于更多,只是将期货保持在列表中。 – Fildor

+4

请不要用这种方式来思考线程。有些事情正在完成,如果需要超过一定的时间,则需要超时。这不是什么线索可能发生在做这项工作,它是*工作*。也许两个线程正在进行合作。当时可能没有线程正在处理那项工作。但它是你想要停止或超时的*工作*,而不是可能或可能不会发生的线程或线程。这种细微的观点转变对正确思考线程非常重要,这样您就可以收到良好的设计。 –

+1

@DavidSchwartz好点,但是可能有些作品是完全独立于彼此的情况。我不知道OP是否确实是这样,但在这里看起来就是这样。 – fge

回答

3

你见过? ExecutorService.invokeAll

它应该正是你想要的:调用一捆工作人员,并让他们超时如果花费太长时间。评论后

编辑 - (新概念): 可以使用CountDownLatch等待任务,通过完成await(long timeout, TimeUnit unit)和超时! 然后,您可以连做一个shutdownNow时,看看哪些任务已花费的时间太长......

编辑2:

这样可以很清楚:

  1. 拥有的CountDownLatch通过每个劳工进行倒计时, 等结束了。
  2. 在主执行线程await上所述锁存器超时。
  3. 当该调用返回时,您可以检查锁存器的计数以查看是否存在超时命中(如果它> 0)。
  4. a)count = 0,所有任务都按时完成。 b)如果没有,循环期货并检查其isDone。您不必在ExecutorService上调用关闭。
  5. 如果您不再需要执行程序,请致电关机。

注:工作人员可以在超时的同时完成,并呼吁他们未来的isDone()。

+0

好的......不过,如果线程完成了,我必须调用“executor.shutdown()”,对吗? – sk2212

+0

不一定。您仍然可以在每个未来中循环调查isDone()或调用get(),或者您可以使用http://docs.oracle.com/javase/7/docs/api/java/util/concurrent /CountDownLatch.html我认为后者是“最好”的方式。 – Fildor

+0

啊,好吧。 “CountDownLatch”看起来非常好。你说(在你的“编辑”行中)我可以在调用await之后运行shutdownNow。那么shutdownNow方法会取消所有因设置超时而仍在运行的线程? 这是我的下一个问题,因为我发现无法用invokeAll找出哪些线程“违反”超时。也许用新的方法是可能的? – sk2212