1. 正常情况下,一个线程怎样退出
- 正常情况下,通过线程的
start()
方法启动一个线程后,线程开始执行run()
方法 run()
方法运行结束后线程退出
2. 接上题,那为什么还需要结束一个线程呢,或者说需要特别结束一个线程的场景有哪些
- 很多线程运行的模式是死循环,比如在生产者/消费者模式中,消费者主体就是一个死循环,它不停地从队列中接受任务、执行任务。在停止程序时,我们需要一种优雅的方法以关闭该线程
- 在一些图形用户界面程序中,线程是用户启动的,完成一些任务,比如从远程服务器上下载一个文件,在下载过程中,用户可能会希望取消该任务
- 在一些场景中,比如从第三方服务器查询一个结果,我们希望在限定的时间内得到结果,如果得不到,我们会希望取消该任务
- 有时,我们会启动多个线程做同一件事,比如类似抢火车票,我们可能会让多个好友帮忙从多个渠道买火车票,只要有一个渠道买到了,我们会通知取消其他渠道
3. 取消/关闭一个线程的机制
- Java 的
Thread
类定义了如下方法:public final void stop()
,但被标记为了过时,不应该再使用 - 在 Java 中,停止一个线程的主要机制是中断
- 中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出
4. Thread
类中定义的关于中断的方法有哪些,它们的区别是
- 每个线程都有一个标志位,表示该线程是否被中断了
- 关于中断的方法有如下几个
public boolean isInterrupted()
:返回对应线程的中断标志位public void interupt()
:表示中断对应的线程public static boolean interrupted()
:返回当前线程的中断标志位是否为true
,但它还有一个重要的副作用,就是清空中断标志位。即,连续两次调用interrupted()
,第一次返回的结果为true
,第二次一般就是false
(除非同时又发生了一次中断)
5. 线程对中断的反应是怎样的
interrupt()
对线程的影响与线程的状态和在进行的 IO 操作有关。我们主要考虑线程的状态,IO 操作的影响和具体 IO 以及操作系统有关线程的状态有:
RUNNABLE
:线程在运行或具备运行条件只是在等待操作系统调度WAITING/TIMED_WAITING
:线程在等待某个条件或超时BLOCKED
:线程在等待锁,试图进入同步块NEW/TERMINATED
:线程还未启动或已结束
RUNNABLE
- 如果线程在运行中,且没有执行 IO 操作,
interupt()
只是会设置线程的中断标志位,没有任何其他作用 - 线程应该在运行过程中合适的位置检查中断标志位。比如,如果主体代码是一个循环,可以在循环开始处进行检查
- 如果线程在运行中,且没有执行 IO 操作,
WAITING/TIMED_WAITING
- 线程调用
join()/wait()/sleep()
方法会进入WAITING
或TIMED_WAITING
状态。在这些状态时,对线程对象调用interrupt()
会使得该线程抛出InterruptedException
异常。抛出异常后,中断标志位会被清空,而不是被设置 InterruptedException
是一个受检异常,线程必须进行处理。处理异常的基本思路是:如果知道怎么处理,就进行处理,如果不知道,就应该向上传递,通常情况下不应该捕获异常后然后忽略- 捕获到
InterruptedException
异常时,通常希望结束该线程
- 线程调用
BLOCKED
- 如果线程在等待锁,对线程对象调用
interrupt()
只是会设置线程的中断标志位,线程依然会处于BLOCK
状态 interrupt()
并不能使一个在等待锁的线程真正“中断”,即interrupt()
方法只会设置线程的中断标志,而并不会使它从锁等待队列中出来- 在使用
synchronized
关键字获取锁的过程中不响应中断请求,这是synchronized
的局限性。如果这对程序是一个问题,应该使用显示锁Lock
接口,显示锁接口Lock
支持以响应中断的方式获取锁
- 如果线程在等待锁,对线程对象调用
NEW/TERMINATER
- 如果线程尚未启动(
NEW
),或者已经结束(TERMINATER
),则调用interrupt()
对它没有任何效果,中断标志位也不会被设置
- 如果线程尚未启动(
6. 实际开发中,怎样正确地取消/关闭线程
interrupt()
方法不一定会真正“中断”线程,它只是一种协作机制- 如果不明白线程在做什么,不应该贸然调用线程的
interrupt()
方法 - 对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,外部调用者应该调用这些方法而不是直接调用
interrupt()
方法