zoukankan      html  css  js  c++  java
  • Thread 如何安全结束一个线程 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
    MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

    目录

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

    Thread类相关的方法

    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方法来终止线程。

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

    • 1、任务中一般都会有循环结构,只要用一个标记控制住循环,就可以结束任务。
    • 2、如果线程处于了冻结状态,无法读取标记,此时可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。

    使用退出标志

    当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。

    在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。

    public class Test {
        public static volatile boolean exit = false;//退出标志
    
        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("线程结束了");
                };
            }.start();
    
            try {
                Thread.sleep(1000 * 5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            exit = true;//5秒后更改退出标志的值
        }
    }

    使用 interrupt 方法

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

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

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

    使用 interrupt() + InterruptedException

    线程处于阻塞状态,如Thread.sleep、wait、IO阻塞等情况时,调用interrupt方法后,sleep等方法将会抛出一个InterruptedException:

    public class Test {
        public static void main(String[] args) {
            Thread thread = new Thread() {
                public void run() {
                    System.out.println("线程启动了");
                    try {
                        Thread.sleep(1000 * 100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程结束了");
                }
            };
            thread.start();
    
            try {
                Thread.sleep(1000 * 5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread.interrupt();//作用是:在线程阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态
        }
    }

    使用 interrupt() + isInterrupted()

    public class Test {
        public static void main(String[] args) {
            Thread thread = new Thread() {
                public void run() {
                    System.out.println("线程启动了");
                    while (!isInterrupted()) {
                        System.out.println(isInterrupted());//调用 interrupt 之后为true
                    }
                    System.out.println("线程结束了");
                }
            };
            thread.start();
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            thread.interrupt();
            System.out.println("线程是否被中断:" + thread.isInterrupted());//true
        }
    }

    一个综合案例

    public class Test {
        static boolean flag = true;
    
        public static void main(String[] args) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始休眠");
                    try {
                        Thread.sleep(100 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("结束休眠,开始死循环");
                    while (flag) {
                    }
                    System.out.println("------------------子线程结束------------------");
                }
            });
            thread.start();
    
            Scanner scanner = new Scanner(System.in);
            System.out.println("输入1抛出一个中断异常,输入2修改循环标志位,输入3判断线程是否阻塞,输入其他结束Scanner
    ");
            while (scanner.hasNext()) {
                String text = scanner.next();
                System.out.println("你输入了:" + text + "
    ");
                if ("1".equals(text)) {
                    thread.interrupt();
                } else if ("2".equals(text)) {
                    flag = false; //如果不设为false,主线程结束后子线程仍在运行
                } else if ("3".equals(text)) {
                    System.out.println(thread.isInterrupted());
                } else {
                    scanner.close();
                    break;
                }
            }
            System.out.println("------------------主线程结束------------------");
        }
    }

    不能结束的情况

    注意下面这种是根本不能结束的情况!

    public class Test {
        public static void main(String[] args) {
            Thread thread = new Thread() {
                public void run() {
                    System.out.println("线程启动了");
                    while (true) {//对于这种情况,即使线程调用了intentrupt()方法并且isInterrupted(),但线程还是会继续运行,根本停不下来!
                        System.out.println(isInterrupted());//调用interrupt之后为true
                    }
                }
            };
            thread.start();
            thread.interrupt();//注意,此方法不会中断一个正在运行的线程,它的作用是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态
            while (true) {
                System.out.println("是否isInterrupted:" + thread.isInterrupted());//true
            }
        }
    }

    2019-5-8

  • 相关阅读:
    两数交换不借助第三变量
    linux磁盘文件系统管理
    Fedora15设置开机进入终端模式
    快速构建Windows 8风格应用8贴靠视图
    快速构建Windows 8风格应用6GridView数据控件
    快速构建Windows 8风格应用4FlipView数据控件
    快速构建Windows 8风格应用9竖直视图
    快速构建Windows 8风格应用1开发工具安装及模拟器使用
    快速构建Windows 8风格应用5ListView数据控件
    快速构建Windows 8风格应用2创建调试应用
  • 原文地址:https://www.cnblogs.com/baiqiantao/p/5654263.html
Copyright © 2011-2022 走看看