如何中断java/scala中提交给newsinglethreadexecutor的线程?

643ylb08  于 2021-07-09  发布在  Java
关注(0)|答案(1)|浏览(394)

假设我有以下测试代码:

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() 但是,根据我的测试,上述方法不能中断线程。
所以我想知道有没有强制中断线程的方法。

e1xvtsh3

e1xvtsh31#

根据javadocs Future#cancel 以及 ExecutorService#shutdownNow ,典型的实现是这些方法导致中断底层线程。
如果任务已经启动,那么mayInterruptFrunning参数确定是否应该中断执行此任务的线程以尝试停止任务。
除了尽最大努力停止处理正在执行的任务之外,没有其他保证。例如,典型的实现将通过thread.interrupt()取消,因此任何未能响应中断的任务可能永远不会终止。
特别注意最后的评论。通过 Thread#interrupt 方法是一个合作的过程。当一个线程中断另一个线程时,会导致设置目标线程的中断状态。另外,如果目标线程在某些特定方法中被阻塞,那么该线程将经历 InterruptedException .
如果在目标线程中执行的代码都没有通过 Thread#isInterrupted 方法或调用阻塞方法和句柄 InterruptedException ,则中断实际上不起任何作用。该代码在中断过程中不合作,因此实际上没有办法关闭它,尽管线程中断。

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

理想情况下,打算在后台线程中执行的长时间运行的代码将被更改为在线程中断中进行协作。在你的例子中,一个可行的方法就是改变 sum 检查 Thread#isInterrupted 每n次迭代 for 循环,如果中断,则中止循环。然后,它可以抛出一个异常来指示它没有完成,或者可能返回一些sentinel BigInt 值来指示中止(如果合适)。
如果调用的代码确实无法更改,则不能通过线程中断来停止它。您可以潜在地使用守护程序线程,以便至少这些线程不会在关闭期间阻止jvm退出。

相关问题