如何正确的关闭一个线程:
这个听起来比较简单的问题其实解决起来并不那么容易;
如何关闭线程:
- 首先需要强调的是stop suspend 两个方式是极力不推荐使用的,JDK8已经被标记为Deprecated;
- 那么关闭线程的方法就只能是线程自己运行完逻辑并退出了,我们外界能做的就是调用interrupt方法给一个信号,那么就需要代码检测这个信号并做退出逻辑;
那么什么情况下线程不会正常退出:
-
线程的run方法本身是一个死循环,没有检测interrupt信号,如下:
Thread t1 = new Thread(){ @Override public void run() { super.run(); int i = 0; while(true) { ++i; } } };
这种线程是肯定不会自己退出的;
-
线程执行了等待或者阻塞的方法,无法响应Interrupt信号,如下:
ReentrantLock lock = new ReentrantLock(); Thread t2 = new Thread(){ @Override public void run() { super.run(); int i = 0; while(!Thread.interrupted()) { lock.lock(); } } };
这是因为ReentrantLock的lock方法并不能响应中断导致的即便外界对线程进行interrupt,该lock.lock()操作还是会继续WAITING,导致线程不能及时退出;
解决方法:
针对第一种情况,不要使用while(true),使用while(!Thread.interrupted()),或者使用isInterrupted方法,他们的区别在于interrupted会清零标志位,即第一次调用interrupted可能返回true,第二次会返回false;而调用isInterrupted不会清零标志位,多次调用都会返回true;值得注意的是interrupted()是static方法,而isInterrupted方法是实例的方法,但其实底层都是调用了实例方法isInterrupted(boolean)方法,传的形参决定了是否清理标志位;
对于阻塞方法的调用,尽量使用可中断版本,如ReentrantLock的lockInterruptibly版本,并捕捉InterruptedException,catch中做好退出逻辑;
注意事项:
interrupt方法只是设置了标志位,而不一定会抛出InterruptedException,只有当被中断的线程执行了可中断的方法时,标志位被置位,才会抛出InterruptedException;如列子中的第一段代码,因为其是纯计算逻辑,就算调用interrupt方法, 其也不会抛出InterruptedException而终止循环;
如果Thread.interrupted()检测并清零了标志位,后续的可中断操作不会再抛出InterruptedException,除非再一次调用interrupt方法;
反过来也是,如果可中断操作抛出InterruptedException了也会清零标志位,没有再次调用interrupt方法的前提下,Thread.interrupted()将不会再返回true;