Java中正确终止线程的方法
Thread类中有一个已经废弃的 stop() 方法,它可以终止线程,但由于它不管三七二十一,直接终止线程,所以被废弃了。比如,当线程被停止后还需要进行一些善后操作(如,关闭外部资源),使用这个方法就无能为力了。可以通过线程中断来实现线程终止。
首先来看一下Java线程中断的一些内容:
- Java平台为每个线程维护了一个布尔型的中断标记,可以通过下列方法获取该标记的值:
- interrupt() 中断某个线程
- isInterrupted() 返回该线程的中断标记
- interrupted() 返回并重置该线程的中断标记(置为false)
- 中断仅是发起线程对目标线程的一种请求,也就是说,目标线程对这种请求可以相应,也可以忽略。
- Java标准库中与线程阻塞相关的方法对中断的相应方式都是抛出 InterruptedException 异常,并且按照惯例,抛出异常前都会重置中断标记为false,因此这些方法会清空线程的中断标记。
- Java标准库中与线程阻塞相关的方法在进行阻塞前会判断中断标记是否为true,为true则抛出异常;如果在阻塞后调用中断方法的话,那么JVM会设置该线程的中断标记,然后将该线程唤醒,因此中断具有唤醒线程的作用。
由上面几点和第二句加粗的话可知,可以使用线程中断来实现线程终止,只要目标线程判断一下中断标记即可,即使被中断的线程正处于阻塞状态,也能把他唤醒起来终止;由第一句加粗的话可知,直接使用线程中断实现线程终止是存在风险的,因为可能调用了一些Java标准库的阻塞方法,而导致了中断标记被清空,也就无法获得中断标记了(总是false),因此需要自己创建一个中断标记配合使用。
如,下面是一个可中断的任务执行器,他会在每次执行任务前,判断一下自定i的终止标记和剩余的任务数(善后);提供的shutdown方法除了将工作线程中断外(主要作用是唤醒可能处于阻塞状态的任务),还会将终止交集 terminated 置为 true。
执行 main 方法,可以发现,首先会打印出“客户端调用了 shutdown 方法”,然后过了四秒,main线程才会终止,可知shutdown方法正确地将目标线程终止了。关于“按照惯例,Java标准库中抛出InterruptedException异常的和线程相关的阻塞方法会清空中断标记”,可以将条件中的 !interminated 替换成 !Thread.currentThread().isInterrupted(),然后再执行main方法测试,可以发现main线程始终无法终止,因为 sleep() 方法清空了中断标记,所以 !Thread.currentThread().isInterrupted() 始终为true,导致工作线程始终无法终止。
public class TerminableTaskRunner {
// 存储要执行的任务
private final BlockingQueue<Runnable> tasks;
// 线程终止标志
private volatile boolean terminated;
// 剩余的任务数
private final AtomicInteger count;
// 实际执行任务的线程
private volatile Thread workThread; public TerminableTaskRunner(int capacity) {
this.tasks = new LinkedBlockingDeque<>(capacity);
this.count = new AtomicInteger(0);
this.workThread = new WorkThread();
workThread.start();
} public void submit(Runnable task) {
this.tasks.add(task);
this.count.incrementAndGet();
} public void shutdown() {
terminated = true; // 线程终止标志,由于中断标志可能会被覆盖,所以需要自己创建一个标志
if (workThread != null)
workThread.interrupt(); // 唤醒线程
} private class WorkThread extends Thread {
@Override
public void run() {
Runnable task;
try {
while (!terminated || tasks.size() >= 1) {
task = tasks.take();
try {
task.run(); // 可能会清空当前线程的中断标记,如task.run()在内部调用的阻塞方法抛出了InterruptedException
} catch (Throwable e) {
e.printStackTrace();
}
count.decrementAndGet();
}
} catch (InterruptedException e) {
// 一旦调用shutdown且tasks.take()阻塞住,就抛出该异常,没有任务要执行,直接终止
workThread = null;
}
}
} public static void main(String[] args) {
TerminableTaskRunner taskRunner = new TerminableTaskRunner(4);
for (int i = 0; i < 4; i++) {
taskRunner.submit(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("客户端调用了 shutdown 方法");
}
});
}
taskRunner.shutdown(); }
}
Java中正确终止线程的方法的更多相关文章
- Java中各种(类、方法、属性)访问修饰符与修饰符的说明
类: 访问修饰符 修饰符 class 类名称 extends 父类名称 implement 接口名称 (访问修饰符与修饰符的位置可以互换) 访问修饰符 名称 说明 备注 public 可以被本项目的所 ...
- Java中的守护线程 & 非守护线程(简介)
Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...
- Java中如何创建线程
Java中如何创建线程 两种方式:1)继承Thread类:2)实现Runnable接口. 1.继承Thread类 继承Thread类,重写run方法,在run方法中定义需要执行的任务. class M ...
- Java中的栈,堆,方法区和常量池
要说Java中的栈,堆,方法区和常量池就要提到HotSpot,HotSpot是Sun JDK 和 Open JDK中所带的虚拟机. (Sun JDK 和 Open JDK除了注释不同,代码实现基本上是 ...
- Java 中extends与implements使用方法
Java 中extends与implements使用方法 标签: javaclassinterfacestring语言c 2011-04-14 14:57 33314人阅读 评论(7) 收藏 举报 分 ...
- Java中的equals和hashCode方法
本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...
- Java中替换HTML标签的方法代码
这篇文章主要介绍了Java中替换HTML标签的方法代码,需要的朋友可以参考下 replaceAll("\\&[a-zA-Z]{0,9};", "").r ...
- java中需要关注的3大方面内容/Java中创建对象的几种方法:
1)垃圾回收 2)内存管理 3)性能优化 Java中创建对象的几种方法: 1)使用new关键字,创建相应的对象 2)通过Class下面的new Instance创建相应的对象 3)使用I/O流读取相应 ...
- Java中字符串的一些常见方法
1.Java中字符串的一些常见方法 /** * */ package com.you.model; /** * @author Administrator * @date 2014-02-24 */ ...
随机推荐
- 解决mariadb字符级不是utf8问题
解决方法:在my.cnf配置文件中添加如下几个指令 [client] default-character-set=utf8 [mysqld] character-set-server=utf8 in ...
- 01_垂直居中body中的应用
1: 应用场景 在body中书写一个代码块, 使其相对于body垂直居中 <!DOCTYPE html> <html lang="en"> <head ...
- 基于GPS北斗卫星授时系统和NTP网络授时服务器的设计与开发
基于GPS北斗卫星授时系统和NTP网络授时服务器的设计与开发 安徽京准科技提供@请勿转载@@ 更多资料请参考——ahjzsz.com 天文观测设备对于控制系统的时间准确度有严格要求.为此,采用搭建高精 ...
- Quartz.NET总结(八)如何根据自己需要配置Topshelf 服务
前面讲了如何使用Topshelf 快速开发windows服务, 不清楚的可以看之前的这篇文章:https://www.cnblogs.com/zhangweizhong/category/771057 ...
- Serv_U FTP服务端使用教程
Serv-U FTP Server是一种被广泛运用的FTP服务器端软件,可以设定多个FTP服务器.限定登录用户的权限.登录主目录及空间大小等,功能非常完备.具有非常完备的安全特性,支持SSl FTP传 ...
- SpringBoot2 整合Nacos组件,环境搭建和入门案例详解
本文源码:GitHub·点这里 || GitEE·点这里 一.Nacos基础简介 1.概念简介 Nacos 是构建以"服务"为中心的现代应用架构,如微服务范式.云原生范式等服务基础 ...
- Project Settings之Quality翻译
(版本是2018.4......翻译是自己的渣翻译水平) Unity allows you to set the level of graphical quality it attempts to r ...
- 大白话建造者模式(Builder Pattern)
前言 起初打算按照之前的日产系列写建造者模式.但参考了网上的很多文章,让我对建造者模式更加的困惑,也害怕自己无法已易懂的方式进行解释.最后通过Google发现了一篇英文文章Builder,使我茅塞顿开 ...
- 原生JS在网页上复制的所有文字后面自动加上一段版权声明
不少技术博客有这样的处理,当我们复制代码的时候,会自动加上一段本信息版权为XXXX,这是怎么实现的呢? 其实实现的方式很简单,可以在我的网站页面上绑定一个copy事件,当你复制文章内容的时候,自动在剪 ...
- 19南京网络赛B 欧拉降幂
题目链接 给a,b,p.有b个a的幂 #include <iostream> using namespace std; typedef long long LL; const LL N = ...