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. linux下修改jar中的文件

    解压修改后再打包 解压: jar xvf xxx.jar 打包: jar cvfm0 xxx.jar META-INF/MANIFEST.MF ./ 注: -m参数可以将 一个具体的mainfest文 ...

  2. [Java复习] 分布式事务 Part 2

    分布式事务了解吗?如果解决分布式事务问题的? 面试官心里: 只要聊到你做了分布式系统,必问分布式事务,起码得知道有哪些方案,一般怎么来做,每个方案的优缺点是什么. 为什么要有分布式事务? 分布式事务实 ...

  3. Faster RCNN论文学习

    Faster R-CNN在Fast R-CNN的基础上的改进就是不再使用选择性搜索方法来提取框,效率慢,而是使用RPN网络来取代选择性搜索方法,不仅提高了速度,精确度也更高了 Faster R-CNN ...

  4. React之简介

    官网链接React 用于构建用户界面的 JavaScript 库 特色 声明式: React 使创建交互式 UI 变得轻而易举.为你应用的每一个状态设计简洁的视图,当数据改变时 React 能有效地更 ...

  5. float和int转换

    http://blog.sina.com.cn/s/blog_5c6f79380101bbrd.html https://blog.csdn.net/ganxingming/article/detai ...

  6. 【408】C函数中的ADT

    类似类的形式 boardADT.h:所有的宏.声明等部分写在这里 boardADT.c:只需要 #inclue "boardADT.h",不需要 include 其他系统头文件,然 ...

  7. PAT 甲级 1040 Longest Symmetric String (25 分)(字符串最长对称字串,遍历)

    1040 Longest Symmetric String (25 分)   Given a string, you are supposed to output the length of the ...

  8. JEECG中修改时间相关自定义定时器

    JEECG中使用,如下: @InitBinder public void initBinder(ServletRequestDataBinder binder) { binder.registerCu ...

  9. 第十五章 单点登录——《跟我学Shiro》

    目录贴:跟我学Shiro目录贴 Shiro 1.2开始提供了Jasig CAS单点登录的支持,单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个 ...

  10. Azure AADSTS7000215 其中一种问题的解决

    众所周知,Azure提供了整套的rest api,经过认证和授权,完美阐述了”我是谁,我能做什么“.对资源层的操作,我们很多时候是使用Powershell或者Azure CLI或者各个语言的SDK, ...