这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
所谓上山容易下山难,有了前面的抛砖引玉,自然而然就能有一系列的问题。前面的创建线程、以及正确的启动线程方式,接下来面试官便是有头有尾,来~讲一下怎么停止一个线程。
停止线程
所谓停止线程,就是让一个正在处理任务的线程,停止继续执行,放弃当前任务的操作。
关于停止线程,应该是用interrupt去通知线程停止,而不是强制停止。注意,这里强调的是通知,即第三方线程发出停止线程的通知,至于是否停止,由被通知线程去决定。
最佳实践:如何正确停止线程
-
优先抛出
在run()方法中调用了其他方法,如下在run()方法中调用了reInMethod()方法,子方法(reInMethod()方法)应该优先抛出异常(throw new Interruption()),不能在子方法中内部try/catch 解决异常,否则在run()方法中无法终止线程。
-
恢复中断
如若一定要在子方法中try/catch 捕获异常,则需要在catch内恢复中断,使run()方法判断成功,不应屏蔽中断。
@Override
public void run() {
while (true ){
if (Thread.currentThread().isInterrupted()){
System.out.println("Interrupted 程序运行结束");
break;
}
reInMethod();
System.out.println("保存日志");
}
}
private void reInMethod() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
停止线程的错误方法
-
stop() → 错误的原因在于 调用stop() 方法会释放掉所有的线程(Orcle官网可知),会破坏线程的运行周期 案例:银行转账10笔钱,第二笔的时候调用stop()方法,中间的金额部分调用失败,导致复盘追责困难
如下代码案例中,连队中,一部分士兵获取到武器,但是调用stop()方法后,致使一个连队的一部分士兵有武器,一部分没有,导致后期追查困难。
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("连队"+i+"开始分发武器");
for (int j = 0; j < 10; j++) {
System.out.println("士兵"+j+"拿到");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("连队"+i+"完成分发");
}
}
public static void main(String[] args) {
Thread thread = new Thread(new StopThread());
thread.start();
try {
Thread.sleep(10);
thread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
-
suspend()、resume()方法并不会释放锁,如果没有其他线程及时解锁,将会导致死锁问题。
-
利用volatile设置boolean标识位作为中断线程
利用BlockingQueue构造一个阻塞队列,并生成一个volatile的boolean类型标识位,容易导致线程死锁但不会中断线程 是因为 volatile标识位依赖于while的校验,但是 线程的死锁发生在阻塞队列的storage的put方法。 例:
- 陷入阻塞中,volatile是无法阻断线程的
- 此例中,生产者的生产速度很快,消费者速度慢
- 所以阻塞队列满后,生产者会阻塞,等待消费者进一步消费
//阻塞队列
BlockingQueue storage;
public volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (num<=100000 && !canceled) {
if (num % 100 == 0) {
System.out.println("num是100的倍数,被放到队列中");
storage.put(num);
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("生产者停止运行");
}
}