如何在Java / Scala中中断提交给newSingleThreadExecutor的线程?

时间:2017-07-14 15:21:41

标签: java multithreading scala

鉴于我有以下测试代码:

import java.util.concurrent._

object TestTime {
  def main(args: Array[String]) {
    println("starting....")
    val service = Executors.newSingleThreadExecutor
    val r = new Callable[Unit]() {
      override def call(): Unit = {
        //your task
        val t0 = System.nanoTime
        val total = sum(1000000000)
        val t1 = System.nanoTime
        println("Elapsed time " + (t1 - t0) / 1e9 + " secs")
        println(s"total = $total")
      }
    }
    val f = service.submit(r)
    try {
      // attempt the task for 2 second
      f.get(2, TimeUnit.SECONDS)
    } catch {
      case _: TimeoutException =>
        f.cancel(true)
        println(s"Timeout....")
    } finally {
      service.shutdown()
    }
    println("after 2 seconds....")
    for(i <- 1 to 2){
      println(s"$i ...")
      Thread.sleep(1000)
    }
    println("main thread ends...")
  }

 //Given that sum() is written by others and I cannot change it.
 def sum(k: Int): BigInt = {
    var total: BigInt = 0
    for (i <- 1 to k) {
      total += i
    }
    total
  }
}

我想最多执行sum 2秒。如果超过时间限制,应立即中断相应的线程。为了中断该线程,我在catch TimeoutException时尝试了两种方法:

  

f.cancel(true)

     

service.shutdownNow()

但是,根据我的测试,上述方法无法中断线程。

所以我想知道是否存在强制中断线程的方法。

enter image description here

1 个答案:

答案 0 :(得分:5)

根据Future#cancelExecutorService#shutdownNow的JavaDocs,典型的实现是这些方法导致中断底层线程。

  

如果任务已经启动,那么mayInterruptIfRunning参数确定执行此任务的线程是否应该在尝试停止任务时被中断。

     

除了尽力尝试停止处理主动执行任务之外,没有任何保证。例如,典型的实现将通过Thread.interrupt()取消,因此任何无法响应中断的任务都可能永远不会终止。

特别注意最后的评论。通过Thread#interrupt方法的线程中断是一个合作过程。当一个线程中断另一个线程时,它会导致设置目标线程的中断状态。此外,如果目标线程在某些特定方法中被阻止,那么该线程将遇到InterruptedException

如果目标线程中执行的代码既没有通过Thread#isInterrupted方法定期检查中断状态,也没有调用阻塞方法并处理InterruptedException,那么中断实际上什么都不做。该代码在中断过程中没有合作,因此尽管线程中断,实际上没有办法将其关闭。

//Given that sum() is written by others and I cannot change it.

理想情况下,用于在后台线程中执行的长时间运行的代码将被更改为在线程中断中协作。在您的示例中,一种可行的技术是在sum循环的每N次迭代中更改Thread#isInterrupted以检查for,如果中断,则中止循环。然后,它可以抛出异常以指示它没有完成或可能返回一些标记BigInt值以指示中止是否合适。

如果真正无法更改调用的代码,则无法通过线程中断将其停止。您可以使用daemon threads,这样至少这些线程在关闭期间不会阻止JVM退出。