线程启动完毕后,在运行可能需要终止,Java提供的终止方法只有一个stop,但是不建议使用此方法,因为它有以下三个问题:

(1)stop方法是过时的

从Java编码规则来说,已经过时的方式不建议采用.

(2)stop方法会导致代码逻辑不完整

stop方法是一种"恶意" 的中断,一旦执行stop方法,即终止当前正在运行的线程,不管线程逻辑是否完整,这是非常危险的.

看如下代码:

 1 public class Client {
 2     public static void main(String[] args) throws Exception {
 3         // 子线程
 4         Thread thread = new Thread() {
 5             @Override
 6             public void run() {
 7                 try {
 8                     // 该线程休眠1秒
 9                     Thread.sleep(1000);
10                 } catch (InterruptedException e) {
11                     //异常处理
12                 }
13                 System.out.println("此处代码不会执行");
14             }
15         };
16         // 启动线程
17         thread.start();
18         // 主线程休眠0.1秒
19         Thread.sleep(100);
20         // 子线程停止
21         thread.stop();
22
23     }
24 }

这段代码的逻辑,子线程是一个匿名内部类,它的run方法在执行时会休眠1秒钟,然后再执行后续的逻辑,而主线程则是休眠0.1秒后终止子线程的运行,也就是说,JVM在执行thread.stop()时,子线程还在执行sleep(1000),此时stop方法会清除栈内信息,结束该线程,这也导致了run方法的逻辑不完整,输出语句println代表的是一段逻辑,可能非常重要,比如子线程的主逻辑,资源回收,情景初始化等等,但是因为stop线程了,这些就都不再执行了,于是就产生了业务逻辑不完整的情况.

这是极度危险的,因为我们不知道子线程会在什么时候停止,stop连基本的逻辑完整性都无法保证,而且此种操作也是非常隐蔽的,子线程执行到何处会被关闭很难定位,这为以后的维护带来了很多的麻烦.

(3)stop方法会破坏原子逻辑

多线程为了解决共享资源抢占的问题,使用了锁的概念,避免资源不同步,但是正是因为此原因,stop方法却会带来更大的麻烦,它会丢弃所有的锁,导致原子逻辑受损.例如 有这样一段程序:

 1 public class Client {
 2     public static void main(String[] args) {
 3         MultiThread t = new MultiThread();
 4         Thread t1 = new Thread(t);
 5         // 启动t1线程
 6         t1.start();
 7         for (int i = 0; i < 5; i++) {
 8             new Thread(t).start();
 9         }
10         // 停止t1线程
11         t1.stop();
12     }
13 }
14
15 class MultiThread implements Runnable {
16     int a = 0;
17
18     @Override
19     public void run() {
20         // 同步代码块,保证原子操作
21         synchronized ("") {
22             // 自增
23             a++;
24             try {
25                 // 线程休眠0.1秒
26                 Thread.sleep(100);
27             } catch (InterruptedException e) {
28                 e.printStackTrace();
29             }
30             // 自减
31             a--;
32             String tn = Thread.currentThread().getName();
33             System.out.println(tn + ":a =" + a);
34         }
35     }
36 }

MultiThread实现了Runnable接口,具备多线程的能力,run方法中加入了synchronized代码块,表示内部是原子逻辑,a的值会先增加后减少,按照synchronized的规则,无论启动多少个线程,打印出来的结果都应该是a=0,

但是如果有一个正在执行的线程被stop,就会破坏这种原子逻辑.(上面main方法中代码)
首先说明的是所有线程共享了 一个MultThread的实例变量t,其次由于在run方法中加入了同步代码块,所以只能有一个线程进入到synchronized块中.

此段代码的执行顺序如下:

1)线程t1启动,并执行run方法,由于没有其他线程同步代码块的锁,所以t1线程执行自加后执行到sleep方法开始休眠,此时a=1.

2)JVM又启动了5个线程,也同时运行run方法,由于synchronized关键字的阻塞作用,这5个线程不能执行自增和自减操作,等待t1线程释放线程锁.

3)主线程执行了t1.stop方法,终止了t1线程,注意由于a变量是线程共享的,所以其他5个线程获得的a变量也是1.

4)其他5个线程获得CPU的执行机会,打印出a的值.

结果是:

Thread-5:a =1
Thread-4:a =1
Thread-3:a =1
Thread-2:a =1
Thread-1:a =1

原本期望synchronized同步代码块中的逻辑都是原子逻辑,不受外界线程的干扰,但是结果却出现原子逻辑被破坏的情况,这也是stop方法被废弃的一个重要原因:破坏了原子逻辑.

既然终止一个线程不能用stop方法,那怎样才能终止一个正在运行的线程呢?

使用自定义的标志位决定线程的执行情况,代码如下:

 1 import java.util.Timer;
 2 import java.util.TimerTask;
 3
 4 public class Client {
 5     public static void main(String[] args) throws InterruptedException {
 6         final SafeStopThread sst = new SafeStopThread();
 7         sst.start();
 8         //0.5秒后线程停止执行
 9         new Timer(true).schedule(new TimerTask() {
10             public void run() {
11                 sst.terminate();
12             }
13         }, 500);
14     }
15
16 }
17
18 class SafeStopThread extends Thread {
19     //此变量必须加上volatile
20     private volatile boolean stop = false;
21     @Override
22     public void run() {
23         //判断线程体是否运行
24         while (stop) {
25             // Do Something
26             System.out.println("Stop");
27         }
28     }
29     //线程终止
30     public void terminate() {
31         stop = true;
32     }
33 }

在线程主题中判断是否需要停止运行,即可保证线程体的逻辑完整性而且也不会破坏原值逻辑.

Thread还提供了一个interrupt中断线程的方法,这个不是过时的方法,是否可以使用这个中断线程?

很明确的说,interrupt不能终止一个正在执行着的线程,它只是修改中断标志位而已.例如:

 1 public class Client {
 2     public static void main(String[] args) {
 3         Thread t1 = new Thread() {
 4             public void run() {
 5                 //线程一直运行
 6                 while (true) {
 7                     System.out.println("Running……");
 8                 }
 9             }
10         };
11         // 启动t1线程
12         t1.start();
13         System.out.println(t1.isInterrupted());//false
14         // 中断t1线程
15         t1.interrupt();
16         System.out.println(t1.isInterrupted());//true
17     }
18 }

执行这段代码,会一直有Running在输出,永远不会停止,执行了interrupt没有任何的变化,那是因为interrupt方法不能终止一个线程状态,它只会改变中断标志位.

在t1.interrupt()前后加上了t1.isInterrupted()会发现分别输出的是false和true.

如果需要终止该线程,还需要执行进行判断,例如我们可以使用interrupt编写出更加简洁,安全的终止线程的代码:

 1 import java.util.Timer;
 2 import java.util.TimerTask;
 3
 4 public class Client {
 5     public static void main(String[] args) throws InterruptedException {
 6         final SafeStopThread sst = new SafeStopThread();
 7         sst.start();
 8         //0.5秒后线程停止执行
 9         new Timer(true).schedule(new TimerTask() {
10             public void run() {
11                 sst.interrupt();
12             }
13         }, 500);
14     }
15
16 }
17
18 class SafeStopThread extends Thread {
19     @Override
20     public void run() {
21         //判断线程体是否运行
22         while (!isInterrupted()) {
23             // Do Something
24         }
25     }
26 }

总之,如果期望终止一个正在运行的线程,则不能使用已经过时的stop方法,需要执行编码实现.这样保证原子逻辑不被破坏,代码逻辑不会出现异常.

当然还可以使用线程池,比如ThreadPoolExecutor类,那么可以通过shutdown方法逐步关闭线程池中的线程,它采用的是比较温和,安全的关闭线程方法,完全不会产生类似stop方法的弊端.

转载于:https://www.cnblogs.com/DreamDrive/p/5623804.html

[改善Java代码]不使用stop方法停止线程相关推荐

  1. [改善Java代码]覆写equals方法必须覆写hashCode方法

    覆写equals方法必须覆写hashCode方法,这条规则基本上每个Javaer都知道,这也是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?本建议就来解释该问题,我们先 ...

  2. java 代码解析工具_改善 Java 代码质量的工具与方法

    原标题:改善 Java 代码质量的工具与方法 我们可能见过上面的有关代码质量的图片,究竟如何衡量一段代码好坏? 代码质量是什么?为什么它很重要? 作家通过他的著作来讲述了一个清晰的.令人信服的故事.他 ...

  3. 改善Java代码有哪些方法?

    前言 Java是一门优秀的面向对象的编程语言,针对遇到同样的一个问题会有很多中解法哪种实现方法是最好的呢,还需要不断的探究JDK的底层原理.我会例出Java改善的建议哦,希望大家可以在平时开发工作去使 ...

  4. Eclipse远程调试Java代码的三种方法

    Eclipse远程调试Java代码的三种方法, 第1种方法是用来调试已经启动的Java程序,Eclipse可以随时连接到远程Java程序进行调试, 第2种方法可以调试Java程序启动过程,但是Ecli ...

  5. eclipse 远程调试java_Eclipse远程调试Java代码的三种方法

    Eclipse远程调试Java代码的三种方法, 第1种方法是用来调试已经启动的Java程序,Eclipse可以随时连接到远程Java程序进行调试, 第2种方法可以调试Java程序启动过程,但是Ecli ...

  6. [改善Java代码]在接口中不要存在实现代码

    第3章  类.对象及方法 书读得多而不思考,你会觉得自己知道的很多. 书读得多而思考,你会觉得自己不懂的越来越多. -伏尔泰 在面向对象编程(Object-Oriented Programming,O ...

  7. idea java代码格式化_intellij Idea中的Java代码格式化(链式方法调用)

    我对Intellij Idea 14.1.4中的 java代码格式有一个小问题. 我有一段由我手动格式化的代码,对我来说很好看: public class Test { private static ...

  8. java 不可修改的集合对象_[改善Java代码]asList方法产生的List对象不可更改

    上一个建议之处了asList方法在转换基本类型数组时候存在的问题,在看下asList方法返回的列表有何特殊的地方.看代码: importjava.util.Arrays;importjava.util ...

  9. [改善Java代码]自由选择字符串拼接方法

    对一个字符串拼接有三种方法:加号,contact方法,StringBuffer或者StringBuilder的append方法,其中加号是最常用的.其他两种方式偶尔会出现在一些开源项目中,那么这三者有 ...

最新文章

  1. classpath详解
  2. 工业机器人专项检测技术——环境检测
  3. kinana 清空索引数据_(Elasticsearch)实战Elasticseartch、Logstash、Kibana
  4. 非线性时延系统matlab框图,非线性主-从时延系统的时滞相关有限时间同步控制方法与流程...
  5. Django——日志
  6. python storm连接mysql_python ORM storm中的复合外键引用
  7. android 开发者模式进入
  8. 底量超顶量超级大黑马指标源码_清华女教授忠言:只要出现“底量超顶量”走势,后期必有暴走趋势...
  9. 运行jar文件,jar程序闪退,cmd命令行黑框一闪而过,无法启动jar文件
  10. 聚焦网络攻击|知道创宇云安全2018年度网络安全态势报告
  11. 不要说珍重,不要说再见,就这样,默默地离开。在炎炎的夏季,也正是因为有了思念,才有了久别重逢的欢畅
  12. 网络营销之网络炒作案例分析、精髓及方法讨论
  13. H5 捕鱼游戏搭建教程
  14. 递归边界条件不足的解决方法
  15. 数据库6:连接查询和嵌套查询
  16. 线性筛(Linear Sieve)
  17. vue项目yarn初始化项目报错error D:\xxx\node_modules\node-sass;终极解决方案
  18. 分布式一致性协议:拜占庭将军问题
  19. 行为金融(五):非有效市场
  20. iOS 部署cocoPos简书

热门文章

  1. 动态顺序字符串基本操作实验_掌握套路,你也会用动态规划
  2. pythonide机制_强化vim打造python的IDE
  3. python异常处理_汇总三大python异常处理、自定义异常、断言原理与用法分析
  4. keras fine-tune方法
  5. 【阿里云课程】如何从零开始完成第一个GAN项目
  6. 【AI白身境】深度学习必备图像基础
  7. 全球及中国数字内容产业运行现状调研与盈利前景分析报告2022年
  8. 中国钠离子电池行业竞争需求状况及投资盈利分析报告2021-2027年版
  9. 2.Linux技能要求
  10. js---BOW---页面打开方式,跳转方式 2017-03-24