【一道概率很高的面试题】:
- 如何优雅的结束一个线程?
上传一个大文件,正在处理费时的计算,如何优雅的结束这个线程呢?
【stop方法】:
【为何不建议使用stop呢?】:
因为很容易产生数据不一致的问题;
stop方法不管你线程处于什么状态,如果持有很多把锁,它会二话不说释放所有的锁,并且不会做善后的工作。原先的锁如果是为了数据的一致性,那么使用stop有可能就会打破数据的一致性。
public class T01_Stop {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
System.out.println("go on");
SleepHelper.sleepSeconds(1);
}
});
t.start();
SleepHelper.sleepSeconds(5);
t.stop(); //不建议大家使用。
}
}
【 suspend_resume 】:
【这两个方法被废掉的原因】:
原因和stop非常的类似 , 万一暂停的瞬间持有一把锁 , 这一把锁是不会被释放的,如果你忘记了恢复,那么就缔造出了一把《永恒的锁》。所以也是会产生数据不一致的问题的。
public class T02_Suspend_Resume {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (true) {
System.out.println("go on");
SleepHelper.sleepSeconds(1);
}
});
t.start();
SleepHelper.sleepSeconds(5);
t.suspend(); //暂停;
SleepHelper.sleepSeconds(3);
t.resume(); //恢复执行。
}
}
【volatile结束线程】:
【不太适合的情况】:
循环中有个数统计的计数器。
循环中有wait、recv、accept等方法,wait会阻塞住,不能进行下一次循环,也无法结束。
【适合的情况】:
不太依赖中间的状态——如上传数据,多上传一些 / 少上传一些 都不影响的这种情况。
public class T03_VolatileFlag {
private static volatile boolean running = true;
public static void main(String[] args) {
Thread t = new Thread(() -> {
long i = 0L;
while (running) {
//wait recv accept
i++;
}
System.out.println("end and i = " + i); //4168806262 4163032200
});
t.start();
SleepHelper.sleepSeconds(1);
running = false;
}
}
【interrupt结束线程】:
//interrupt比volatile更加优雅一些 , 因为如果是volatile的话 , 中间有一些wait / sleep 的话 , 你是停不了的 , 无法跳到下一次循环。但如果是interrupt的话,你只要在sleep / wait 里面处理 InterruptedException 也可以正确的结束这个线程。
【仍有缺点】:
10个元素的容器 , 精确到第5个 , 这个也是无法达到的!!!
【这种情况如何正常结束呢?】:
必须生产者Thread和外面打断它的Thread , 两者配合才可以。
【其他方法】:
内部线程进行定时的检查 , 每隔多长时间检查一次标志位 , 每经过一次循环检查一次标志位。
/**
* interrupt是设定标志位
*/
public class T04_Interrupt_and_NormalThread {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.interrupted()) { //只要标记位没被设置,我就一直循环~ ~ ~
//sleep wait
}
System.out.println("t1 end!");
});
t.start();
SleepHelper.sleepSeconds(3);
t.interrupt();
}
}
public class T05_InterruptAndPark {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("1");
LockSupport.park();
System.out.println("2");
});
t.start();
SleepHelper.sleepSeconds(2);
t.interrupt();
}
}
【阶段总结】:
【volatile 和 interrupt】:
总之,你不依赖于中间精确的次数和精确的时间的话 , 它俩都行。
【结束线程的方法】:
- 自然结束(能自然结束就尽量自然结束)
- stop() suspend() resume()
- volatile标志
- 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
- 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,
但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
- interrupt() and isInterrupted(比较优雅)