首先对于线程的停止有一个原则需要记住:想要停止一个线程,应该是让该线程知道他需要停止【通过协作的方式】,而不是强制停止。
下面就来说一下如何做到通知线程应该要停止了。
使用interrupt停止线程
package ltd.personalstudy.threadbasic;/*** @Author 咖啡杯里的茶* @date 2020/12/6*/public class StopThread implements Runnable{@Overridepublic void run() {int count = 0;// 检查线程的标识位,没有被别的线程中断并且满足业务要求就继续执行while (!Thread.currentThread().isInterrupted() && count < 1000) {System.out.println("count=" + count++);}}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new StopThread());thread.start();Thread.sleep(5);thread.interrupt(); // 中断线程}}
isInterrupted()不会修改线程中断标识位
sleep过程中能否感知到线程中断
package ltd.personalstudy.threadbasic;import java.sql.Struct;/*** @Author 咖啡杯里的茶* @date 2020/12/6*/public class StopThread implements Runnable{@Overridepublic void run() {int count = 0;while (!Thread.currentThread().isInterrupted() && count < 1000) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("count=" + count++);}System.out.println(Thread.currentThread().isInterrupted());}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new StopThread());thread.start();Thread.sleep(5);thread.interrupt();}}
执行结果
- 在sleep、wait等可以让线程进入阻塞的方法使线程休眠了,处于休眠状态的线程被中断后,线程是可以感知到中断信号的,并且会抛出一个InterruptedException,同时会清楚中断信号
最佳处理方式
直接抛出异常
再次中断

错误的线程停止方法
- stop()
- 该方法会直接停止线程,不够友好
- suspend()
- 该方法调用之后线程会挂起,但是在调用resume之前是不会释放锁的,这样容器导致死锁,所以现在也不使用了。
- resume()
上面的三个方法以及被java废弃了,使用这三个方法来停止线程是错误的。
使用volatile修饰标记位
可以使用volatile的情形

如果所示,上面的代码使用volatile修饰的变量来停止线程是可以的,可以的原因就是在while循环里面的代码是没有发生阻塞的,如果在里面的代码发生了阻塞,即使外面修改volatile的标识位,但是由于不会进入下一次循环,所以也是不能停止线程的。
volatile不能正确停止的事例
package ltd.personalstudy.threadbasic;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;/*** @Author 咖啡杯里的茶* @date 2020/12/6*/public class StopThread {public static void main(String[] args) throws InterruptedException {ArrayBlockingQueue storage = new ArrayBlockingQueue(8);Producer producer = new Producer(storage);Thread producerThread = new Thread(producer);producerThread.start();Thread.sleep(500);Consumer consumer = new Consumer(storage);while (consumer.needMoreNums()) {System.out.println(consumer.storage.take() + "被消费了");Thread.sleep(100);}System.out.println("消费者不需要更多的数据了");producer.canceled = true;System.out.println(producer.canceled);}}class Producer implements Runnable {public volatile boolean canceled = false;BlockingQueue storage;public Producer(BlockingQueue storage) {this.storage = storage;}@Overridepublic void run() {int num = 0;try {while (!canceled && num < 1000000) {if (num % 50 == 0) {System.out.println(num + "是50的倍数,被放到仓库中了");storage.put(num);}num++;Thread.sleep(1);}} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("生产者线程执行结束");}}}class Consumer {BlockingQueue storage;public Consumer(BlockingQueue storage) {this.storage = storage;}public boolean needMoreNums() {if (Math.random() > 0.97) {return false;}return true;}}
在上面的代码中生产者和消费者使用了同一个阻塞队列,在生产者的run方法中有这么一段代码
如果代码在这里面阻塞了,那么即使外面修改了canceled变量,也会因为由于阻塞而不能进入下一次while循环导致不能正确的停止线程。
