如何安全的结束一个正在运行的线程

背景


java.lang.Thread类包含了一些常用的方法,如:start(), stop(), stop(Throwable) ,suspend(), destroy() ,resume()。通过这些方法,我们可以对线程进行方便的操作,但是这些方法中,只有start()方法得到了保留。

在JDK帮助文档以及Sun公司的一篇文章《Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?》中都讲解了舍弃这些方法的原因。

简单来说是因为:使用stop方法虽然可以强行终止正在运行或挂起的线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。

那么,我们究竟应该如何停止线程呢?

使用退出标志


当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。

public class Test {     public static volatile boolean exit = false;//退出标志。volatile关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值     public static void main(String[] args) {         new Thread() {             public void run() {                 System.out.println("线程启动了");                 while (!exit) {//死循环,正常情况下是不会停止的                     try {                         Thread.sleep(1000);                     } catch (InterruptedException e) {                         e.printStackTrace();                     }                     System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));                 }                 System.out.println("线程安全停止了");             };         }.start();         new Thread() {//另开一条线程             public void run() {                 try {                     Thread.sleep(1000 * 5);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 exit = true;//5秒后更改退出标志的值             };         }.start();     } }

使用interrupt

如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?

这种情况经常会发生,比如当一个线程由于需要等候键盘输入而被阻塞,或者调用Thread.join()方法,或者Thread.sleep()方法,在网络中调用ServerSocket.accept()方法,或者调用了DatagramSocket.receive()方法时,都有可能导致线程阻塞,使线程处于处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。

这里我们给出的建议是,不要使用stop()方法,而是使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码。

使用interrupt方法来终端线程可分为两种情况: 1、线程处于阻塞状态,如Thread.sleep、wait、IO阻塞等情况时。在这种情况下调用interrupt方法后,sleep方法将抛出一个InterruptedException public class Test {     public static void main(String[] args) throws InterruptedException {         Thread thread = new Thread() {             public void run() {                 System.out.println("线程启动了");                 try {                     Thread.sleep(1000 * 10);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println("哈哈,被中断了:" + isInterrupted());//这里的结果竟然是false             }         };         thread.start();         Thread.sleep(1000 * 1);         thread.interrupt();//注意,此方法不会中断一个正在运行的线程,它的作用是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态         Thread.sleep(1000 * 1);         System.out.println("线程是否被中断:" + thread.isInterrupted());//这里的结果竟然是false     } }  
2、使用来判断线程是否被中断 public class Test {     public static void main(String[] args) throws InterruptedException {         Thread thread = new Thread() {             public void run() {                 System.out.println("线程启动了");                 while (!isInterrupted()) {                     System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + "--" + isInterrupted());//调用之后为true                 }                 System.out.println("哈哈,被中断了");             }         };         thread.start();         Thread.sleep(1000 * 2);         thread.interrupt();//注意,此方法不会中断一个正在运行的线程,它的作用是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态         System.out.println("线程是否被中断:" + thread.isInterrupted());//true     } }

不能结束的情况

注意下面这种即使异常也根本不能结束的情况!

public class Test {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread() {

            public void run() {

                System.out.println("线程启动了");

                while (true) {//对于这种情况,即使线程调用了intentrupt()方法并且isInterrupted(),但线程还是会继续运行,根本停不下来!

                    System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + "--" + isInterrupted());//调用之后为true

                }

            }

        };

        thread.start();

        Thread.sleep(1000 * 5);

        thread.interrupt();//注意,此方法不会中断一个正在运行的线程,它的作用是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态

        while (true) {

            System.out.println("是否被中断:" + thread.isInterrupted());//true

        }

    }

}

来自为知笔记(Wiz)

时间: 2024-11-07 03:40:37

如何安全的结束一个正在运行的线程的相关文章

如何取消一个正在运行的线程

============问题描述============ 我现在想实现的功能是,点击左边9个按钮中的任一个,创建10个线程向服务器请求10张图片,我现在遇到的麻烦就是,当我快速而且连续随机点击左边的按钮时,那不是会创建几十甚至上百个线程啊(已测试,线程过多直接导致手机死掉了),而我只想要最后一次点击按钮请求的图片.大家帮我想想怎么办啊,我是该只创建一个线程请求完一张图片后,再去创建线程请求下一张,还是怎么办啊!我不知道怎么kill掉一个正在运行的线程,注:该线程无循环. 大家有什么好的思路都说说

如何中断一个正在运行的线程

有时候中断一个正在运行的线程是一个比较重要的应用,但是貌似也是比较困难的事情.假设我们正在用第三方支付软件进行支付,点击确定以后, 由于某些原因需要取消这次支付,貌似现在还没有哪家的支付软件能够实现这样的功能,因为这个实在太困难了.开启一个线程去进行支付功能后, 即使做了一个这样的中断这个线程的功能,这个功能也不能保证钱在没有支付的前提下,线程被扼杀. 因为在Java的语言层面上没有提供有保证性的能够安全的终止线程的方法.而是采用了一种协商的方式来对线程进行中断.interrupte()方法就是

Java再学习——停止一个正在运行的线程

关于这个问题,先了解一下Thread类方法中被废弃的那些方法.suspend(), resume(),stop()/stop(Throwable obj),destroy() 首先,stop(Throwable obj)和destroy()方法在最新的Java中直接就不支持了,没必要去看了.我们只需瞧瞧suspend(), resume(), stop()这三个就行了; suspend()——让当前线程暂停执行 resume()——让当前线程恢复执行 当调用suspend()的时候,线程并没有释

[视频讲解]Java(JDK的下载安装及第一个程序运行)

(JDK的下载安装及第一个程序运行) 内容:Java JDK 的安装以及HelloWorld 程序的运行 欢迎童鞋们前往围观 http://v.youku.com/v_show/id_XODA3MzkzNzMy.html 更多内容分享请关注 我的博客 http://www.xiaozhangwx.com 本视频由 小张网校 提供

winForm程序开机启动和托盘显示,并允许一个程序运行

1.建一个WinForm程序—TestIconForm,将其属性ShowInTaskbar改为false,这样程序将不会在任务栏中显示:将MaximizeBox属性设置为false,屏蔽掉最大化按钮:把StartPosition属性改为CerternScreen,这样程序运行后,窗口将会居中显示. 2.在工具栏中的公共控件里,拖入NotifyIcon控件—NotifyIcon1,这个是程序运行任务栏右侧通知区域图标显示控件. 3.在工具栏中的菜单和工具栏里,拖入ContextMenuStrip—

opengl 一个可以运行的立方体贴图

对于开始学习opengl的人来说 下载一个可以运行的代码,对自己的鼓舞极大,但是网上下载的程序大多数由于这样那样的原因不能运行.有时会抱怨:照抄的啊 咋不行呢?其实原因很简单 主要是环境变了 库配置不对 版本不对 有的库是debug 而有的是release 所以导致不能运行. 下面这个例子是纹理贴图: 图片必须是能被2整除的 比如 128X128  512X512 文件名:MF.bmp 保证你运行而且可以手动控制旋转 ,由于开始贴在正面,所以你看到的是平面.按D键 立即旋转. / fangkua

开源一个跨平台运行的服务插件 - TaskCore.MainForm

本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.MainForm改良成跨平台的服务共大家使用和相互交流:本来这篇应该分享的是nginx+iis+redis+Task.MainForm构建分布式架构后续文章,但使用.netcore来定义一个服务插件和跨平台测试经过让我既兴奋又头痛,不说了下次再分享分布式后续文章吧:那么开始今天的分享内容,希望各位多多支持:

linux 下查看一个进程运行路径

在linux下查看进程大家都会想到用 ps -ef|grep XXX 可是看到的不是全路径,怎么看全路径呢? 每个进程启动之后在 /proc下面有一个于pid对应的路径 例如:ps -ef|grep python 显示:oracle    4431  4366  0 18:56 pts/2    00:00:00 python Server.py 4431就是进程号 到/proc/4431下,ls -l 会看到(需要root权限): 总用量 0 -r--r--r--    1 oracle  

Sqlserver Sql Agent Job 只能同时有一个实例运行

Sqlserver Sql Agent中的Job默认情况下只能有一个实例在运行,也就是说假如你的Sql Agent里面有一个正在运行的Job叫"Test Job",如果你现在再去启动一次"Test Job"就会报错,因为Sqlserver规定在一个Sqlserver账号下,Sql Agent不能同时启动相同的Job两次,只有前一次启动执行完成后,才能够再次启动该Job. 那么随之而来的问题是如何用sql语句检测Job是否已经执行完毕了呢?下面有一个语句可以作为参考: