Thread之十:停止线程方法汇总
在上篇文章《多线程的使用——Thread类和Runnable接口》中提到中断线程的问题。在JAVA中,曾经使用stop方法来停止线程,然而,该方法具有固有的不安全性,因而已经被抛弃(Deprecated)。那么应该怎么结束一个进程呢?官方文档中对此有详细说明:《为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。在此引用stop方法的说明:
- package chapter2;
- public class ThreadFlag extends Thread
- {
- public volatile boolean exit = false;
- public void run()
- {
- while (!exit);
- }
- public static void main(String[] args) throws Exception
- {
- ThreadFlag thread = new ThreadFlag();
- thread.start();
- sleep(5000); // 主线程延迟5秒
- thread.exit = true; // 终止线程thread
- thread.join();
- System.out.println("线程退出!");
- }
- }
在上面代码中定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值。
- package com.jvm.study.thread;
- public class TestThread implements Runnable{
- boolean stop = false;
- public static void main(String[] args) throws Exception {
- Thread thread = new Thread(new TestThread(),"My Thread");
- System.out.println( "Starting thread..." );
- thread.start();
- Thread.sleep( 3000 );
- System.out.println( "Interrupting thread..." );
- thread.interrupt();
- System.out.println("线程是否中断:" + thread.isInterrupted());
- Thread.sleep( 3000 );
- System.out.println("Stopping application..." );
- }
- public void run() {
- while(!stop){
- System.out.println( "My Thread is running..." );
- // 让该循环持续一段时间,使上面的话打印次数少点
- long time = System.currentTimeMillis();
- while((System.currentTimeMillis()-time < 1000)) {
- }
- }
- System.out.println("My Thread exiting under request..." );
- }
- }
- package com.jvm.study.thread;
- public class TestThread2 implements Runnable {
- boolean stop = false;
- public static void main(String[] args) throws Exception {
- Thread thread = new Thread(new TestThread2(), "My Thread2");
- System.out.println("Starting thread...");
- thread.start();
- Thread.sleep(10000);
- System.out.println("Interrupting thread...");
- thread.interrupt();
- System.out.println("线程是否中断:" + thread.isInterrupted());
- Thread.sleep(3000);
- System.out.println("Stopping application...");
- Thread.sleep(30000);
- }
- public void run() {
- while (!stop) {
- System.out.println("My Thread is running...");
- // 让该循环持续一段时间,使上面的话打印次数少点
- long time = System.currentTimeMillis();
- while ((System.currentTimeMillis() - time < 1000)) {
- }
- if (Thread.currentThread().isInterrupted()) {
- return;
- }
- }
- System.out.println("My Thread exiting under request...");
- }
- }
注意:interrupted与isInterrupted方法的区别(见API文档)
当线程处于下面的Blocked状态,通过调用interrupt()方法来停止线程:
2.1、当线程处于下面的状况时((1)如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException异常。这个时候,我们可以通过捕获InterruptedException异常来终止线程的执行,具体可以通过return等退出或改变共享变量的值使其退出。),属于非运行状态:
当sleep方法被调用。
当wait方法被调用。
当被I/O阻塞,可能是文件或者网络等等。
当线程处于上述的状态时,使用前面介绍的方法就不可用了。这个时候,我们可以使用interrupt()来打破阻塞的情况,如:
- public void stop() {
- Thread tmpBlinker = blinker;
- blinker = null;
- if (tmpBlinker != null) {
- tmpBlinker.interrupt();
- }
- }
当interrupt()被调用的时候,InterruptedException将被抛出,所以你可以再run方法中捕获这个异常,让线程安全退出:
- try {
- ....
- wait();
- } catch (InterruptedException iex) {
- throw new RuntimeException("Interrupted",iex);
- }
2.2、阻塞的I/O((2)如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。这时候处理方法一样,只是捕获的异常不一样而已)
当线程被I/O阻塞的时候,调用interrupt()的情况是依赖与实际运行的平台的。在Solaris和Linux平台上将会抛出InterruptedIOException的异常,但是Windows上面不会有这种异常。所以,我们处理这种问题不能依靠于平台的实现。如:
- package com.cnblogs.gpcuster
- import java.net.*;
- import java.io.*;
- public abstract class InterruptibleReader extends Thread {
- private Object lock = new Object( );
- private InputStream is;
- private boolean done;
- private int buflen;
- protected void processData(byte[] b, int n) { }
- class ReaderClass extends Thread {
- public void run( ) {
- byte[] b = new byte[buflen];
- while (!done) {
- try {
- int n = is.read(b, 0, buflen);
- processData(b, n);
- } catch (IOException ioe) {
- done = true;
- }
- }
- synchronized(lock) {
- lock.notify( );
- }
- }
- }
- public InterruptibleReader(InputStream is) {
- this(is, 512);
- }
- public InterruptibleReader(InputStream is, int len) {
- this.is = is;
- buflen = len;
- }
- public void run( ) {
- ReaderClass rc = new ReaderClass( );
- synchronized(lock) {
- rc.start( );
- while (!done) {
- try {
- lock.wait( );
- } catch (InterruptedException ie) {
- done = true;
- rc.interrupt( );
- try {
- is.close( );
- } catch (IOException ioe) {}
- }
- }
- }
- }
- }
另外,我们也可以使用InterruptibleChannel接口。 实现了InterruptibleChannel接口的类可以在阻塞的时候抛出ClosedByInterruptException
。如:
- package com.cnblogs.gpcuster
- import java.io.BufferedReader;
- import java.io.FileDescriptor;
- import java.io.FileInputStream;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.nio.channels.Channels;
- public class InterruptInput {
- static BufferedReader in = new BufferedReader(
- new InputStreamReader(
- Channels.newInputStream(
- (new FileInputStream(FileDescriptor.in)).getChannel())));
- public static void main(String args[]) {
- try {
- System.out.println("Enter lines of input (user ctrl+Z Enter to terminate):");
- System.out.println("(Input thread will be interrupted in 10 sec.)");
- // interrupt input in 10 sec
- (new TimeOut()).start();
- String line = null;
- while ((line = in.readLine()) != null) {
- System.out.println("Read line:'"+line+"'");
- }
- } catch (Exception ex) {
- System.out.println(ex.toString()); // printStackTrace();
- }
- }
- public static class TimeOut extends Thread {
- int sleepTime = 10000;
- Thread threadToInterrupt = null;
- public TimeOut() {
- // interrupt thread that creates this TimeOut.
- threadToInterrupt = Thread.currentThread();
- setDaemon(true);
- }
- public void run() {
- try {
- sleep(10000); // wait 10 sec
- } catch(InterruptedException ex) {/*ignore*/}
- threadToInterrupt.interrupt();
- }
- }
- }
这里还需要注意一点,当线程处于写文件的状态时,调用interrupt()不会中断线程。
三、不提倡的stop()方法
臭名昭著的stop()停止线程的方法已不提倡使用了,原因是什么呢?
当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止,并抛出特殊的ThreadDeath()异常。这里的“立即”因为太“立即”了,
在java多线程编程中,线程的终止可以说是一个必然会遇到的操作。但是这样一个常见的操作其实并不是一个能够轻而易举实现的操作,而且在某些场景下情况会变得更复杂更棘手。
Java标准API中的Thread类提供了stop方法可以终止线程,但是很遗憾,这种方法不建议使用,原因是这种方式终止线程中断临界区代码执行,并会释放线程之前获取的监控器锁,这样势必引起某些对象状态的不一致(因为临界区代码一般是原子的,不会被干扰的),具体原因可以参考资料[1]。这样一来,就必须根据线程的特点使用不同的替代方案以终止线程。根据停止线程时线程执行状态的不同有如下停止线程的方法。
四、 处于运行状态的线程停止
处于运行状态的线程就是常见的处于一个循环中不断执行业务流程的线程,这样的线程需要通过设置停止变量的方式,在每次循环开始处判断变量是否改变为停止,以达到停止线程的目的,比如如下代码框架:
- private volatile Thread blinker;
- public void stop() {
- blinker = null;
- }
- public void run() {
- Thread thisThread = Thread.currentThread();
- while (blinker == thisThread) {
- try {
- //业务流程
- } catch (Exception e){}
- }
- }
如果主线程调用该线程对象的stop方法,blinker对象被设置为null,则线程的下次循环中blinker!=thisThread,因而可以退出循环,并退出run方法而使线程结束。将引用变量blinker的类型前加上volatile关键字的目的是防止编译器对该变量存取时的优化,这种优化主要是缓存对变量的修改,这将使其他线程不会立刻看到修改后的blinker值,从而影响退出。此外,Java标准保证被volatile修饰的变量的读写都是原子的。
上述的Thread类型的blinker完全可以由更为简单的boolean类型变量代替。
五、 即将或正在处于非运行态的线程停止
线程的非运行状态常见的有如下两种情况:
可中断等待:线程调用了sleep或wait方法,这些方法可抛出InterruptedException;
Io阻塞:线程调用了IO的read操作或者socket的accept操作,处于阻塞状态。
上面已经讲过。
3 处于大数据IO读写中的线程停止
处于大数据IO读写中的线程实际上处于运行状态,而不是等待或阻塞状态,因此上面的interrupt机制不适用。线程处于IO读写中可以看成是线程运行中的一种特例。停止这样的线程的办法是强行close掉io输入输出流对象,使其抛出异常,进而使线程停止。
最好的建议是将大数据的IO读写操作放在循环中进行,这样可以在每次循环中都有线程停止的时机,这也就将问题转化为如何停止正在运行中的线程的问题了。
4 在线程运行前停止线程
有时,线程中的run方法需要足够健壮以支持在线程实际运行前终止线程的情况。即在Thread创建后,到Thread的start方法调用前这段时间,调用自定义的stop方法也要奏效。从上述的停止处于等待状态线程的代码示例中,stop方法并不能终止运行前的线程,因为在Thread的start方法被调用前,调用interrupt方法并不会将Thread对象的中断状态置位,这样当run方法执行时,currentThread的isInterrupted方法返回false,线程将继续执行下去。
为了解决这个问题,不得不自己再额外创建一个volatile标志量,并将其加入run方法的最开头:
- public void run() {
- if (myThread == null) {
- return; // stopped before started.
- }
- while(!Thread.currentThread().isInterrupted()){
- //业务逻辑
- }
- }
还有一种解决方法,也可以在run中直接使用该自定义标志量,而不使用isInterrupted方法判断线程是否应该停止。这种方法的run代码框架实际上和停止运行时线程的一样。
Thread之十:停止线程方法汇总的更多相关文章
- 转: Java安全停止线程方法
转: http://blog.csdn.net/flyingpig4/article/details/7675557 1.早期Java提供java.lang.Thread类型包含了一些列的方法star ...
- Java安全停止线程方法
1. 早期Java提供java.lang.Thread类型包含了一些列的方法 start(), stop(), stop(Throwable) and suspend(), destroy() and ...
- java线程之停止线程
在Java中有以下3种方法可以终止一个正在运行的线程: 1.使用退出标志,是线程正常退出,也就是run方法完成后线程终止. 2.使用stop方法强制终止线程,但不推荐使用 ...
- Thread的中断机制(interrupt),循环线程停止的方法
一.中断原理 中断线程 线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡.还是等待新的任务或是继续运行至下一步,就取决于这个 ...
- JAVA之旅(十五)——多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止
JAVA之旅(十五)--多线程的生产者和消费者,停止线程,守护线程,线程的优先级,setPriority设置优先级,yield临时停止 我们接着多线程讲 一.生产者和消费者 什么是生产者和消费者?我们 ...
- [改善Java代码]不使用stop方法停止线程
线程启动完毕后,在运行可能需要终止,Java提供的终止方法只有一个stop,但是不建议使用此方法,因为它有以下三个问题: (1)stop方法是过时的 从Java编码规则来说,已经过时的方式不建议采用. ...
- 多线程---其他方法 停止线程、守护线程、join方法
第三方停止线程: 原来是stop(),因为该方法有些问题,所以被interrupt()方法取代,它的用途跟机制是 当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到 ...
- 正确停止线程的方式三 使用Thread类中的内置的中断标记位-----------不熟悉
package charpter10; public class Processor implements Runnable { @Override public void run() { for ( ...
- java中线程的几种状态和停止线程的方法
1.线程的状态图 需要注意的是:线程调用start方法是使得线程到达就绪状态而不是运行状态 2.停止线程的两种方法 1)自然停止:线程体自然执行完毕 2)外部干涉:通过线程体标识 1.线程类中定义线程 ...
随机推荐
- Django运算表达式与Q对象/F对象
Django运算表达式与Q对象/F对象 1 模型查询 概述: 1 查询集:表示从数据库中获取的对象的集合 2 查询集可以有多个过滤器,通过 逻辑运算符连接 3 过滤器就是一个函数,基于所给的参数限制查 ...
- zabbix监控elasticsearch
1.环境概述 虚拟机系统:CentOS Linux release 7.3.1611 (Core) 宿主机系统:Mac Sierra version 10.12.3 nginx:1.10.3 php: ...
- 玩转PHP中的正则表达式
玩转PHP中的正则表达式 检验用户输入.解析用户输入和文件内容,以及重新格式化字符串 级别: 中级 正则表达式提供了一种处理文本的强大方法.使用正则表达式,您可以对用户输入进行复杂的检验.解析用户输入 ...
- [翻译 EF Core in Action 2.3] 理解EF Core数据库查询
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- Python一行代码实现快速排序
上期文章排序算法——(2)Python实现十大常用排序算法为大家介绍了十大常用排序算法的前五种(冒泡.选择.插入.希尔.归并),因为快速排序的重要性,所以今天将单独为大家介绍一下快速排序! 一.算法介 ...
- ssm框架搭建和整合流程
Spring + SpringMVC + Mybatis整合流程 1 需求 1.1 客户列表查询 1.2 根据客户姓名模糊查询 2 整合思路 第一步:整合dao层 ...
- JS实现页面复制文字时自动加版权
经亲自实践,尝试了各种方法,目前可行的方法主要有如下两种: 可以在任何运行使用js代码的网站中使用,比如本人在自己的博客园博客中实现了一下,读者您可亲自在本人博客上测试. 方法1: <scrip ...
- 从git仓库导入idea的gradle项目无法添加依赖包问题
引言 之前将项目导入到git仓库,后来同事从git仓库将项目导入到本地,发现无法导入依赖包. 解决方法 这个勾idea是默认勾上的,意思是idea将默认使用本地离线工作模式,使用的是本地仓库,每次依赖 ...
- 微服务(入门三):netcore ocelot api网关结合consul服务发现
简介 api网关是提供给外部调用的统一入口,类似于dns,所有的请求统一先到api网关,由api网关进行指定内网链接. ocelot是基于netcore开发的开源API网关项目,功能强大,使用方便,它 ...
- HTML 练习js代码位置在head标签
方式一: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...