Java 由于超时而安全取消线程

Java 由于超时而安全取消线程,java,multithreading,concurrency,design-patterns,Java,Multithreading,Concurrency,Design Patterns,我有一个需要执行的任务队列,还有一个工人池,他们负责挑选任务并执行它们。还有一个“管理器”类,用于跟踪工作人员,允许用户停止或重新启动他们,报告他们的进度,等等。每个工作人员执行以下操作: public void doWork() { checkArguments(); performCalculation(); saveResultsToDatabase(); performAnotherCalculation(); saveResultsToDatab

我有一个需要执行的任务队列,还有一个工人池,他们负责挑选任务并执行它们。还有一个“管理器”类,用于跟踪工作人员,允许用户停止或重新启动他们,报告他们的进度,等等。每个工作人员执行以下操作:

public void doWork() {
    checkArguments();
    performCalculation();
    saveResultsToDatabase();
    performAnotherCalculation();
    saveResultsToDatabase();
    performYetAnotherCalculation();
    saveResultsToDatabase();
}
在本例中,“数据库”不一定指Oracle数据库。这当然是一个选项,但结果也可以保存在磁盘上,在Amazon SimpleDB中,等等

到目前为止,一切顺利。但是,由于各种因素,performCalculation()代码有时会间歇性锁定,但主要是因为在一堆第三方库中网络代码的实现不佳(f.ex.Socket.read()永远不会返回)。显然,这是不好的,因为这项任务现在永远无法完成,工人现在已经死了

我想做的是将整个doWork()方法包装成某种超时,如果超时过期,将任务交给其他人

但是我怎么能做到呢?假设原始辅助进程被困在“performCalculation()”方法中。然后,我将任务交给其他工作人员,由他们完成任务,然后原始工作人员决定醒来并将其中间结果保存到数据库中。。。因此会破坏完全有效的数据。是否有一些通用模式可以避免这种情况


我可以看到一些解决方案,但其中大多数都需要对所有业务逻辑代码进行认真的重构,从头开始。。。从理论上讲,这可能是正确的做法,但我根本没有时间做这件事。

我想最简单的方法是使用performCalculation()的线程启动时,创建一个单独的计时器线程。计时器线程可以在一段时间后唤醒,然后
thread.interrupt()
计算线程可以在处理InterruptedException时执行任何必要的回滚


诚然,这会增加管理其他问题的复杂性,因此不是最优雅的解决方案。

您是否尝试过使用
未来
?它们对于运行任务并等待任务完成(使用超时等)非常有用。例如:

private Runnable performCalc = new Runnable() { 
  public void run() {
    performCalculation();
  }
}

public void doWork() {
  try {
    ExecutorService executor = Executors.newFixedThreadPool(1);
    executor.submit(performCalc).get(); // Timeouts can be used here.
    executor.submit(anotherCalc).get();
  } catch(InterruptedException e) {
    // Asked to stop. Rollback out transactions.
  } catch(OtherExceptions here) {
  }
}

如果
performCalculation
卡在阻塞IO上,您几乎无法中断它。一种解决方案是使用
socket.setSoTimeout
关闭底层套接字或设置套接字操作超时,但您必须拥有从套接字读取的代码才能执行此操作


否则,您可以在将数据保存到数据库之前添加一些协调机制。使用某种时间戳来检测数据库中的数据是否比原始工作者从网络获取的数据更新

我承认我不完全确定Thread.interrupt是如何工作的。即使线程在本机代码中的某个地方被阻塞(f.ex.Socket.read()或某些nio调用),它也会中断线程吗?没错,Socket.getInputStream().read()不会抛出InterruptedException。I/O操作将抛出
InterruptedException
,这是
IOException
的子类。