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\n");
while (scanner.hasNext()) {
String text = scanner.next();
System.out.println("你输入了:" + text + "\n");
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

Thread 如何安全结束一个线程 MD的更多相关文章

  1. C#如何优雅的结束一个线程

    大家都知道在C#里面,我们可以使用 Thread.Start方法来启动一个线程,当我们想停止执行的线程时可以使用Thread.Abort方法来强制停止正在执行的线程,但是请注意,你确定调用了Threa ...

  2. THRDTERM-----干净地结束一个线程

    THRDTERM产生两个线程.周期性地检查一个event对象.以决定要不要结束自己. #define WIN32_LEAN_AND_MEAN #include<stdio.h> #incl ...

  3. android 如何结束一个线程

    总结: 1 不推荐直接调用onstop()强制结束,,因为不安全 2 run()比较短暂,执行完毕会自动停止 3 在run()设置一个flag标识,满足条件才执行; 4 通过sleep()捕获异常,在 ...

  4. C# 启动和结束一个线程

    在程序执行中会遇到启动本软件的exe问,或者启用其它的exe文件,已达到执行某些操作的作用.下面是两种最常见的启动exe文件. 1.调用系统dll使用其提供的方法. 引用的dll, [DllImpor ...

  5. 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---干净的终止一个线程

    干净的终止一个线程  我曾经在第2章产生一个后台线程,用以输出一张屏幕外的 bitmap 图.我们必须解决的一个最复杂的问题就是,如果用户企图结束程序,而这张bitmap 图尚未完成,怎么办?第2章的 ...

  6. Java 如何正确停止一个线程

    自己在做实验性小项目的时候,发现自己遇到一个问题:如何控制线程的"死亡"? 首先,如何开启一个线程呢? 最简单的代码: public class Main { public sta ...

  7. Java并发(基础知识)—— 创建、运行以及停止一个线程

    在计算机世界,当人们谈到并发时,它的意思是一系列的任务在计算机中同时执行.如果计算机有多个处理器或者多核处理器,那么这个同时性是真实发生的:如果计算机只有一个核心处理器那么就只是表面现象. 现代所有的 ...

  8. 一个线程加一运算,一个线程做减一运算,多个线程同时交替运行--synchronized

    使用synchronized package com.pb.thread.demo5; /**使用synchronized * 一个线程加一运算,一个线程做减法运算,多个线程同时交替运行 * * @a ...

  9. 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ----初始化一个线程

    使用线程的一个常见问题就是如何能够在一个线程开始运行之前,适当地将它初始化.初始化最常见的理由就是为了调整优先权.另一个理由是为了在SMP 系统中设定线程比较喜欢的 CPU.第10 章谈到 MFC 时 ...

随机推荐

  1. mac使用xposed超详细入门级教程Android Studio-20190930

    工具 这里我使用的工具是Android Studio3.4.1,电脑环境mac os mojave 10.14.6(这个应该问题不大) 创建项目 1.打开Android Studio,看到这个界面,并 ...

  2. 命令行启动模块的Python代码研究

    pyrasite的 __requires__ = 'pyrasite==2.0' import re import sys from pkg_resources import load_entry_p ...

  3. 定时检查SetUID 权限文件列表的脚本文件

    [root@localhost ~]# find / -perm -4000 -o -perm -2000 > /root/suid.list #-perm安装权限査找.-4000对应的是Set ...

  4. LayUI使用弹窗重载父级数据表格的两种方法

    参考LayUI官方文档,在子窗口中重载父级数据表格找到以下两种方法: 1.子窗口中重载:在子窗口中直接调用父级talbe的reload方法. $("body").on(" ...

  5. VSCode 代码格式化 快捷键

    Visual Studio Code可以通过以下快捷键 格式化代码: On Windows  Shift + Alt + F On Mac  Shift + Option + F On Ubuntu ...

  6. 【转】Selenium-xpath详解

    1.XPATH是什么 XPATH是一门在XML文档中查找信息的语言,XPATH可用来在XML文档中对元素和属性进行遍历,主流的浏览器都支持XPATH,因为HTML页面在DOM中表示为XHTML文档.X ...

  7. es6 fetch方法请求接口

    fetch(url, { method: 'post', headers: { 'Content-type': 'application/x-www-form-urlencoded; charset= ...

  8. 【Leetcode_easy】686. Repeated String Match

    problem 686. Repeated String Match solution1: 使用string类的find函数: class Solution { public: int repeate ...

  9. unity, 替换shader渲染(Rendering with Replaced Shaders)【转】

    实现特效,尤其是一些后处理特效,经常需要将各物体的shader替换为另一套shader进行渲染到纹理,再后再进行合成或以某种叠加方式叠加到最后的画面上去. 再复杂一点儿的,可能不同的物体所用的替换sh ...

  10. 超级块,i节点,数据块,目录块,间接块

    一.物理磁盘到文件系统 文件系统用来存储文件内容,文件属性,和目录,这些类型的数据如何存储在磁盘块上的呢?Unix/linux使用了一个简单的方法. 他将磁盘分为3个部分: 超级块,文件系统中第一个块 ...