2018-04-02 | learn

Java 如何正确结束线程

我之前只是知道有两种做法

  • while 一个 flag ,通过控制 flag
  • 另一个就是 Thread::intercept(),加 try ... catch

好吧实际工作里基本没有 kill 线程的经验,现在来看看

主要是 interrupt ,因为 while 方法其实算不上什么停止线程,当 while 内的代码块阻塞了,根本走不到下一次 flag 判断。

直接看源码里的注释:

https://developer.android.com/reference/java/lang/Thread.html#interrupt()

  1. 首先会进行检查是否拥有修改当前线程的权限,权限异常会抛出 SecurityException
  2. 调用 interrupt ,如果此时线程阻塞在 Object class 的 wait(), wait(long)), or wait(long, int) 方法 , 或是当前 class 的 join(), join(long), join(long, int), sleep(long), or sleep(long, int)方法,会清除线程 interrupt 状态,并抛出 InterruptedException.
  3. 调用 interrupt ,如果此时线程阻塞在一个 InterruptibleChannel 的 I/O 操作上,channel 将会被关闭,设置线程的 interrupt 状态,线程收到 ClosedByInterruptException
  4. 调用 interrupt ,如果此时线程 block 在一个 Selector,会立即设置线程的 interrupt 状态,并从 selection 操作中返回,也许返回一个非零值,就像执行 selector 的 wakeup 方法一样。
  5. 调用 interrupt ,如果以上情况都不是,会设置线程的 interrupt 状态

直接看第二条和第五条

第 2 条说的是阻塞在这些个方法时调用 interrupt 会抛出 InterruptedException ,这就是我们为啥要加 try ... catch

然后第 5 条,如果以上几条 case 都不是,调用 interrupt 只会设置线程的 interrupt status……(好像知道面试为啥挂了

1
2
3
4
5
// https://stackoverflow.com/a/10962613/4097210
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}

再翻译一下这个 Stack Overflow 的回答

Q: https://stackoverflow.com/questions/10961714/how-to-properly-stop-the-thread-in-java
A: https://stackoverflow.com/a/10962613/4097210

Thread.interrupt() 是个不错的方法。如果你的代码处于可中断的阻塞调用 (比如 Thread.sleep 或是使用 java.nio Channel 操作),你可以从中立即退出。

如果使用 flag,只能等这段阻塞代码跑完,再次进入 flag condition 判断时终止。但有些情况不得不用这个方法,比如使用标准 InputStream/OutputStream api 时,它不是 interruptable 的。

这种情况,调用 interrupt 并不会终止 IO,但是你可以在代码(可以安全 stop 线程并清理资源的位置)加上这么一句话:

1
2
3
4
if (Thread.currentThread().isInterrupted()) {
// cleanup and stop execution
// for example a break in a loop
}

就像我上面说的 Thread.interrupt() 的优势在于可以立即退出,flag 无论如何做不到这点。