Java 如何正确停止一个线程
自己在做实验性小项目的时候,发现自己遇到一个问题:如何控制线程的"死亡"?
首先,如何开启一个线程呢?
最简单的代码:
public class Main { public static void main(String[] args) { Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("当前线程:" + Thread.currentThread() + ",当前时间戳:" + System.currentTimeMillis());
}
}); thread.start(); try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
很简单,调用.start()方法,这个线程就会启动.
那么怎样主动去停止一个线程呢?要解答这个问题,首先要考虑:为什么要结束一个线程.
理由如下:
- 线程是JVM宝贵的资源,有的线程会长时间占用资源.
- 一些业务逻辑下会出现一个线程从逻辑上完全没有意义(比如一个定时器,调用了结束的方法),确实需要去停止.
结束一个线程有一个最基本的方法:Thread.stop()方法:
@Deprecated
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
if (threadStatus != 0) {
resume(); // Wake up thread if it was suspended; no-op otherwise
} // The VM can handle all thread states
stop0(new ThreadDeath());
}
但是这个方法已经是:@Deprecated的了,也就是被建议不要使用的方法.
为什么不建议使用这个方法的官方说明: http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
实际上,我结合自己经验提出以下几点:
- 线程会直接停掉,按照代码逻辑要释放的资源,要调用的接口可能不会被运行(finally块的代码还是会执行)
- 会破坏锁,导致线程不安全(停掉一个线程就会释放它持有的锁,但不能保证逻辑上)
这两点都是非常严重的问题了.即使再小心,去调用stop()也会导致各种各样的问题.
如果不能"硬"实现结束线程,那么就可以考虑下"软"实现.
首先,导致一个线程长时间运行的原因无非有这么几个:
- 代码逻辑混乱\业务复杂,写了一个很长的run()方法
- 长时间睡眠
- 循环
对于这第一种嘛,只能从代码结构上优化了.
而这第二种,就要使用Thread.interrupt()方法了.这个方法唤醒睡眠中的线程:
public class Main { public static void main(String[] args) { Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.DAYS.sleep(1L);
System.out.println("睡眠结束");
} catch (Exception e) {
System.out.println("异常:" + e);
} finally {
System.out.println("finally块被执行");
}
}
}); thread.start(); if (!thread.isInterrupted()) {
thread.interrupt();
}
System.out.println("主线程结束");
}
}
在bio的服务端里面,会阻塞当前线程.监听的端口有消息,才会继续执行,而如果没有人连接,就需要通过这种方式来打断正在监听的线程.
对于这第三种,需要通过轮询标志位来控制退出.自己写的定时器代码:
public class TimerImpl implements Timer { private static final Logger logger = LoggerFactory.getLogger(TimerImpl.class); // 定时器线程
private TimerThread timerThread = null; private volatile Boolean running = false; private Handler taskHandler; private Long time; private TimeUnit unit; @Override
public void start() throws Exception { // 给参数生成本地固定拷贝,以确保在运行过程中,不会给参数的改变干扰
Handler curTask = taskHandler.getClass().newInstance();
Long curTime = new Long(time);
TimeUnit curUnit = unit.getClass().newInstance(); // 检查
if (ParameterUtil.checkNull(curTask, curTime, curUnit)) {
throw new Exception("定时器参数配置错误");
} if (!running) {
synchronized (running) {
if (!running) {
timerThread = new TimerThread(); timerThread.setTaskHandler(curTask);
timerThread.setTime(curTime);
timerThread.setUnit(curUnit); timerThread.start();
running = true;
}
}
}
} @Override
public void stop() throws Exception { if (!running) {
throw new Exception("定时器尚未开始");
} synchronized (running) {
if (running) {
// 标志位
timerThread.cancel();
// 打断睡眠
if (!timerThread.isInterrupted()) {
timerThread.interrupt();
}
running = false;
}
}
} private class TimerThread extends Thread { private volatile boolean stop = false; private Handler taskHandler; private Long time; private TimeUnit unit; @Override
public void run() { // circle
while (!stop) { // sleep
try {
unit.sleep(time);
} catch (InterruptedException e) {
logger.info("定时线程被打断,退出定时任务");
stop = true;
return;
} // do
try {
taskHandler.execute();
} catch (Exception e) {
logger.error("handler执行异常:{}", this.getClass(), e);
}
}
} public void cancel() {
stop = true;
} public Handler getTaskHandler() {
return taskHandler;
} public void setTaskHandler(Handler taskHandler) {
this.taskHandler = taskHandler;
} public Long getTime() {
return time;
} public void setTime(Long time) {
this.time = time;
} public TimeUnit getUnit() {
return unit;
} public void setUnit(TimeUnit unit) {
this.unit = unit;
}
} @Override
public void setTimer(Long time, TimeUnit unit) {
this.time = time;
this.unit = unit;
} @Override
public void setHandler(Handler handler) {
this.taskHandler = handler;
} }
想要停止一个线程的方法是有的,但是会麻烦一些.
Java 如何正确停止一个线程的更多相关文章
- java如何正确停止一个线程
Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的in ...
- Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程
下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...
- Java并发(基础知识)—— 创建、运行以及停止一个线程
在计算机世界,当人们谈到并发时,它的意思是一系列的任务在计算机中同时执行.如果计算机有多个处理器或者多核处理器,那么这个同时性是真实发生的:如果计算机只有一个核心处理器那么就只是表面现象. 现代所有的 ...
- Java再学习——停止一个正在运行的线程
关于这个问题,先了解一下Thread类方法中被废弃的那些方法.suspend(), resume(),stop()/stop(Throwable obj),destroy() 首先,stop(Thro ...
- java停止一个线程
Thread类中有start(), stop()方法,不过stop方法已经被废弃掉. 平时其实也有用过,共享一个变量,相当于标志,不断检查标志,判断是否退出线程 如果有阻塞,需要使用Thread的in ...
- 别指望一文读懂Java并发之从一个线程开始
Understanding concurrent programming is on the same order of difficulty as understanding object-orie ...
- java holdsLock()方法检测一个线程是否拥有锁
http://blog.csdn.net/w410589502/article/details/54949506 java.lang.Thread中有一个方法叫holdsLock(),它返回true如 ...
- JAVA多线程之当一个线程在执行死循环时会影响另外一个线程吗?
一,问题描述 假设有两个线程在并发运行,一个线程执行的代码中含有一个死循环如:while(true)....当该线程在执行while(true)中代码时,另一个线程会有机会执行吗? 二,示例代码(代码 ...
- Java自学-多线程 启动一个线程
Java 创建一个线程的三种方式 多线程即在同一时间,可以做多件事情. 创建多线程有3种方式,分别是继承线程类,实现Runnable接口,匿名类 步骤 1 : 线程概念 首先要理解进程(Process ...
随机推荐
- 【Win10分区教程】
Win10怎么分区?如何为Win10硬盘分区? 注:本教程适用于Win7.Win8.Win8.1和Win10系 到了Windows10时代,TB级硬盘已经很普及了,那么在Win10系统下如何为这些大容 ...
- android Volley+Gson的使用
听说Volley框架非常好用,今天试了一下post请求,果然如此,因为我传的是json获取的也是json所以就写了一种关于json的请求,其实其他的代码都差不多.首先要先创建一个全局的变量,请求入队列 ...
- Vue报错 [Vue warn]: Cannot find element
在前端开发全面进入前端的时代 作为一个合格的前端开发工作者 框架是不可或缺的Vue React Anguar 作为前端小白,追随大佬的脚步来到来到博客园,更新现在正在学习的Vue 注 : 相信学习Vu ...
- Mysql 查看连接数,状态,最大并发数
MySQL: ERROR 1040: Too many connections”的异常情况,造成这种情况的一种原因是访问量过高,MySQL服务器抗不住,这个时候就要考虑增加从服务器分散读压力:另一种原 ...
- Spring BindingResult验证框架Validation特殊用法
使用注解@Valid(实体属性校验) Springboot实现 Spring实现 一.准备校验时使用的JAR validation-api-1.0.0.GA.jar:JDK的接口: hibernate ...
- oracle杀死锁表的进程(转发+合并+自己实践)
之一: Oracle数据库操作中,我们有时会用到锁表查询以及解锁和kill进程等操作 (1)锁表查询的代码有以下的形式:select count(*) from v$locked_object;sel ...
- 【转】Python 数据库连接池
python编程中可以使用pymysql进行数据库连接及增删改查操作,但每次连接mysql请求时,都是独立的去请求访问,比较浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响.因 ...
- 使用uniflash串口烧写CC3200的常见问题
1. 在正常情况下,cc3200的烧写使用的是芯片的PIN55和PIN57,只要把SOP2上拉既可正常烧写,常见问题是烧写的时候没有上拉SOP2,正常运行SOP2留空,IAR只能仿真调试,不能下载程序 ...
- 安装Sql Server 2008时出错
在安装Sql Server 2008的时候,出现一个错误,说是"SQL Server 2005 Express tools"安装了,需要移除,但是我在控制面板里面找了 ...
- 第一篇 Postman的初级使用之设置环境快速切换生成环境与测试环境
POSTMAN是有谷歌的开源工具,在开发调试.测试执行过程中使用频率非常广泛,本文将记录一些postman在测试中常见的一些配置和使用方法 一.基本的页面区域 略,很简单,大家都会看,再有,学习下面的 ...