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

xyz*_*xyz 0 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
  }
}
Run Code Online (Sandbox Code Playgroud)

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

f.cancel(true)

service.shutdownNow()

但是,根据我的测试,上面的方法不能中断线程.

所以我想知道有没有强制中断线程的方法.

在此输入图像描述

Chr*_*oth 5

根据JavaDoc中既Future#cancelExecutorService#shutdownNow,典型的实现,这些方法导致中断的潜在线索.

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

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

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

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

//Given that sum() is written by others and I cannot change it.
Run Code Online (Sandbox Code Playgroud)

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

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