停止线程原则
java没有提供任何机制来安全的中止线程,但是提供了中断(Intertuption)这种协作机制,使用中断interrupt通知需要停止的线程,被终止线程自身拥有决定权(决定是否以及何时停止)。被停止线程本身更清楚在何时停止更为合适,外部强行停止可能使数据处于不一致的情况,使用中断协作方式,需要停止时首先清楚当前正在执行的任务,然后再结束。
实践
1、停止普通线程
被停止线程需要监测线程中断状态
public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2){if(num % 10000 == 0){System.out.println(num + " 为10000的倍数");}num ++;}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}
2、停止阻塞线程
2.1 停止普通阻塞线程
/*** @Author: zhangjx* @Date: 2020/1/14 16:27* 停止sleep线程*/public class RightWayStopThreadWithSleep {public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;while (!Thread.currentThread().isInterrupted() && num <= 300){if(num % 100 == 0){System.out.println(num + " 为100的倍数");}num ++;}try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("线程被中断");}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}}
2.1 循环阻塞响应中断
/*** @Author: zhangjx* @Date: 2020/1/14 16:34* 执行过程中每次循环都会阻塞(sleep或wait) 则不需要每次循环都检测中断,sleep中会检测中断*/public class RightWayStopThreadWithSleepEveryLoop {public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;try{while ( num <= 10000){if(num % 100 == 0){System.out.println(num + " 为100的倍数");}num ++;Thread.sleep(10);}} catch (InterruptedException e) {System.out.println("线程被中断");}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}}
2.3 while中捕获中断
sleep时已经响应过一次中断,中断标记位被清除,无法判定被中断
public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;while ( num <= 10000 && !Thread.currentThread().isInterrupted()){if(num % 100 == 0){System.out.println(num + " 为100的倍数");}num ++;try{Thread.sleep(10);} catch (InterruptedException e) {System.out.println("线程被中断");}}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}
解决方案:
- 方法1 : while中不要try catch InterruptedException异常,try catch 直接包裹整个while
 - 方法2 : while中try catch InterruptedException异常,使用break,跳出while
 - 方法3:传递中断,如果调用其他方法,优先选择在方法上抛出异常,不要使用try catch 捕获异常,以便异常能传到顶层,让run方法可以捕捉这一异常,不要让异常被吃掉。
 
public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;while ( true){try {throwInMeyhod();} catch (InterruptedException e) {System.out.println("线程中断");break;}}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}private static void throwInMethod() throws InterruptedException {Thread.sleep(1000);}
- 方法4:恢复中断 catch中捕获处理,重新打上中断标记
 
public static void main(String[] args) {Thread thread = new Thread(() ->{int num = 0;while ( true ){if(!Thread.currentThread().isInterrupted()){System.out.println("线程被中断");break;}System.out.println("hello");throwInMeyhod();}System.out.println("线程运行完毕");});thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();}private static void throwInMeyhod() {try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
2.4 响应中断的方法总结列表


2.5 错误停止线程的方法
1、被弃用的stop,suspends和resume
stop本质不够安全
suspends和resume 容易死锁
2、volatile关键字设置标记位
部分情况能够实现线程停止,但是如果在线程被阻塞的情况下无法实现
- 无法停止阻塞时的中断,而interrupt设计时已经将长期阻塞作为特殊情况
 
package com.imooc.thread_demo.stopthreads.volatiledemo;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;/*** @Author: zhangjx* @Date: 2020/8/9 15:11* @Description: 陷入阻塞中,volatile无法停止线程*/public class WrongVolatileCantStop {public static void main(String[] args) throws InterruptedException {BlockingQueue queue = new ArrayBlockingQueue(10);Producer producer = new Producer(queue);Consumer consumer = new Consumer(queue);Thread producerThread = new Thread(producer);producerThread.start();/*** 1、当主线程睡眠1000毫秒时 此时阻塞队列满,生产者被阻塞在queue.put(num)处,此时volatile无法停止线程* 2、当主线程睡眠500毫秒时 生产者未被阻塞 到while处判断stop为true则停止*/Thread.sleep(500);while(consumer.needProducer()){System.out.println("数据 " + consumer.queue.take() + "已被消费!");Thread.sleep(100);}System.out.println("消费者不需要更多数据!");producer.stop = true;}}class Producer implements Runnable{public volatile boolean stop = false;private BlockingQueue queue;@Overridepublic void run() {int num = 0;try{while (!stop && num <= Integer.MAX_VALUE / 2){if(num % 100 == 0){queue.put(num);System.out.println(num + " 为100的倍数");}Thread.sleep(1);num ++;}}catch (Exception e){e.printStackTrace();}finally {System.out.println("生产者运行完毕!");}}public Producer(BlockingQueue queue) {this.queue = queue;}}class Consumer{public BlockingQueue queue;public Consumer(BlockingQueue queue) {this.queue = queue;}boolean needProducer(){return false;}}
3、intertupt相关方法辨析
- static boolean interrupted()
 
检测当前线程是否中断,并清除中断位,该方法关心的是执行该方法的线程
boolean isInterrupted()
检测线程Thread对象是否中断,不清除中断位
