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.线程类中定义线程 ...
随机推荐
- 【构造】Bzoj1432[ZJOI2009]Function
Description Input 一行两个整数n; k. Output 一行一个整数,表示n 个函数第k 层最少能由多少段组成. Sample Input 1 1 Sample Output 1 ...
- css3 深入理解flex布局
一.简要介绍 css3最喜欢的新属性之一便是flex布局属性,用六个字概括便是简单.方便.快速. flex( flexible box:弹性布局盒模型),是2009年w3c提出的一种可以简洁.快速弹性 ...
- keras实现简单CNN人脸关键点检测
用keras实现人脸关键点检测 改良版:http://www.cnblogs.com/ansang/p/8583122.html 第一步:准备好需要的库 tensorflow 1.4.0 h5py ...
- 每日分享!JavaScript的鼠标事件(11个事件)
鼠标的11个事件 具体的事件解释如下: click:按下鼠标(通常是按下主按钮)时触发. dblclick:在同一个元素上双击鼠标时触发. mousedown:按下鼠标键时触发. mouseup:释放 ...
- Redis in .NET Core 入门:(4) LIST和SET
第1篇:https://www.cnblogs.com/cgzl/p/10294175.html 第2篇 String:https://www.cnblogs.com/cgzl/p/10297565. ...
- 你真的理解 Spring Boot 项目中的 parent 吗?
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- 学习 JavaScript(二)在 HTML 中使用 JS
基本用法 在 HTML 中使用 <script> 元素引入 Javascript , <script> 有以下 4 个常用属性: async: 异步加载,只对外部脚步有效. d ...
- 为什么需要Docker?
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 估计大家也可能听过Docker这项技术(在论坛上.招 ...
- Java 在PPT中绘制图形
Microsoft PowerPoint可支持在幻灯片中插入各种类型的图形并且可设置图形填充.线条颜色.图形大小.位置等.下面将通过Java编程来演示在PPT中绘制图形的方法. 工具:Free Spi ...
- mybatis入门系列一之创建mybatis程序
Mybatis基础系列一 创建第一个mybatis程序 需要配置项 1. 在conf.xml的需要配置配置两个标签数据库连接和mapper,xml文件加载信息 <-- 进行数据库环境参数的配置 ...