Thread线程停止的正确方式

Thread线程停止的正确方式

前言:革命的本钱是身体,要健康;幸福的根本是平安,要保重;成功的基石是努力,要勤奋;快乐的源泉是开心,要微笑;生活的真谛是洒脱,要享受。

一、概述

当我们使用Thread.stop()方法去停止线程时,这个方法会报错,抛出了UnsupportedOperationException异常,它在JDK中已经被声明“过期/作废”的方法,显然它在功能上有缺陷,不建议使用。

stop()方法的源码:

@Deprecated

public final void stop() {

……

throw new UnsupportedOperationException();

}

该方式是通过抛出ThreadDeath异常来达到停止线程的目的,因此异常抛出可能发生在程序的任何一个地方,报空catch和finally语句快中; 由于抛出ThreadDeath异常,会导致该线程释放所持有的所有锁,而这种释放时间是不可控制的,可能会导致线程安全问题和数据不一样的情况,如在同步代码块中执行数据更新操作时线程突然被停止。

要终止一个线程并不是简单的调用stop()方法,stop()就像linux中的kill一个线程一样,非常暴力,强制线程停止有可能使一些清理工作得不到完成,还有可能会对锁的对象进行解锁,导致数据得不到同步处理,数据不一致,不会保证线程资源的正常释放,可能出现一些不可预期的未知状态,那么避免stop()带来的问题,该怎样去停止线程呢?

二、停止线程的方法

1.使用退出标志

使用退出标志, 使线程正常退出,即当run方法完成后进入线程终止

public void run() {

while(flag){

//do something

}

}

我们在run方法中加入停止线程的标识flag,在一定时间后停止线程(flag为false),自定义Thread类,例如:

public class MyThread implements Runnable {

private int number;

private volatile boolean isStop;//使用volatile关键字修饰,可以在多线程之间共享,成员变量来控制线程的停止

@Override

public void run() {

super.run();

System.out.println(Thread.currentThread().getName() + " == 进入");

while (!isStop) {

synchronized (MyThread.class) {

try {

Thread.sleep(1000);

System.out.println(Thread.currentThread().getName() + " == " + number);

number++;

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

/**

* 设置线程标识

* @param isStop true:停止 false:不停止

*/

public void stopThread(boolean isStop) {

this.isStop = isStop;

}

}

在main方法中执行

public static void main(String[] args) {

try {

//自定义线程

MyThread runnable = new MyThread();

Thread thread = new Thread(runnable, "子线程");

thread.start();

//睡5秒

Thread.sleep(5000);

//停止线程

runnable.stopThread(true);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

根据自定义的MyThread创建子线程,睡5秒后设置线程停止,打印数据如下:

2.使用interrupt()去停止线程

有人会说Thread不是提供了interrupt中断线程的方法吗?很多人认为线程的“中断”就是让线程的停止,在Java中线程的中断(interrupt)只是改变线程的中断状态,对于一般逻辑的线程,如果调用interrupt()方法,对这个线程没有任何影响,但是中断标志变为true,但是并不会停止这个线程的运行。

注意:interrupt()只是改变线程的中断的状态,并没有真正停止线程。

Thread同样提供了相关方法判断线程是否中断:

isInterrupted(): 测试线程是否已经中断,但是不能清除状态标识。interrupted(): 测试当前线程是否中断,静态方法,如果连续执行两次,那么第二次调用将返回false,它执行后具有清除状态的功能,将状态清除变为false;

public static void main(String[] args) {

//当前线程

Thread thread = Thread.currentThread();

thread.interrupt();

//打印interrupted()的状态

System.out.println("interrupted() == " + Thread.interrupted());

System.out.println("interrupted() == " + Thread.interrupted() + "\n");

//打印isInterrupted()的状态

System.out.println("isInterrupted() == " + thread.isInterrupted());

System.out.println("isInterrupted() == " + thread.isInterrupted());

}

打印数据如下:

interrupted()判断当前线程状态是否中断,为什么第一个值为true,第二值为false呢?官方文档中对interrupted()的解释是:测试当前线程是否中断状态,并且将状态信息清除,说明interrupted()返回状态的同时,也将该状态信息清除了。而isInterrupted()则是打印线程是否中断的信息,并没有将该信息清除。

自定义线程:

public class MyThread2 extends Thread {

@Override

public void run() {

super.run();

for (int i = 0; i < 5000; i++) {

if (this.isInterrupted()) {//获取线程的中断状态

System.out.println(Thread.currentThread().getName() + ":isInterrupted():" + this.isInterrupted() + " == " + i);

break;

}

System.out.println(Thread.currentThread().getName() + " == " + i);

}

System.out.println("=== for下面的语句,线程并没有结束 ===");

}

}

main方法中执行:

public static void main(String[] args) {

try {

//自定义线程

MyThread2 thread = new MyThread2();

thread.start();

//睡0.01秒

Thread.sleep(10);

//中断线程(不是真正停止线程,而是改变线程中断状态)

thread.interrupt();

} catch (InterruptedException e) {

e.printStackTrace();

System.out.println("异常信息 == "+e.getMessage());

}

}

打印0-5000的数据,先让Thread2跑了一会就执行interrupt()方法,看看能否中断线程,打印log如下:

从数据可以看到,isInterrupted()默认为false,interrupt()方法将线程的中断状态由false变为true,并且继续往下执行了,所以线程并没有结束。那么我们该怎样去停止线程呢?怎么解决继续执行的问题?

抛异常法

我们可以采用抛异常的方式停止线程(也可以使用return),因为在catch块可以对异常处理进行相关处理,能更好控制流程的运行,不至于流程出现多个return。

自定义线程:

public class MyThread2 extends Thread {

@Override

public void run() {

super.run();

try {

System.out.println();

for (int i = 0; i < 5000; i++) {

if (this.isInterrupted()) {//获取线程的中断状态

System.out.println("isInterrupted():" + this.isInterrupted() + " ========线程已结束,退出线程=========");

//break;

//return;

throw new InterruptedException("抛出异常,线程停止");

}

System.out.println(Thread.currentThread().getName() + ":isInterrupted():" + this.isInterrupted() + " == " + i);

}

System.out.println("=== for下面的语句,线程并没有结束 ===");

} catch (Exception e) {

System.out.println("=== Thread 异常信息 == "+e.getMessage()+" ===");

e.printStackTrace();

}

}

}

main方法:

public static void main(String[] args) {

try {

//自定义线程

MyThread2 thread = new MyThread2();

thread.start();

//睡0.01秒

Thread.sleep(10);

//中断线程(不是真正停止线程,而是改变线程中断状态)

thread.interrupt();

} catch (InterruptedException e) {

e.printStackTrace();

System.out.println("main 异常信息 == "+e.getMessage());

}

}

在打印0-5000的数据中,线程睡10毫秒后将改变线程中断的状态,根据线程中断的状态来抛出异常或者return相关的处理,log如下:

还有一种情况需要注意的,就是在线程睡眠的时候中断线程

Thread类:执行run方法的时候sleep睡眠2秒

public class MyThread3 extends Thread {

@Override

public void run() {

super.run();

try {

System.out.println("=== 线程开始 ===");

Thread.sleep(2000);

System.out.println("=== 线程结束 ===");

} catch (Exception e) {

System.out.println("=== Thread 异常信息 == "+e.getMessage()+" === isInterrupted():"+this.isInterrupted());

e.printStackTrace();

}

}

}

main执行方法:

public static void main(String[] args) {

try {

//自定义线程

MyThread3 thread = new MyThread3();

thread.start();

//中断线程

thread.interrupt();

} catch (Exception e) {

e.printStackTrace();

System.out.println("main 异常信息 == "+e.getMessage());

}

}

打印数据如下:

从控制台的数据可以看到,在睡眠sleep的时候中断线程interrupt(),会立即进入catch的语句,并且将清除停止状态值,变为false,抛出了InterruptedException异常。

对于sleep中的线程,如果你调用了sleep(很长时间),但是现在我想它早点醒过来,那么调用interrupt()使唯一的方法,只有改变它的中断状态,让它从sleep中将控制权转到异常catch语句中,然后再由catch的语句转到正常的逻辑中。

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。

我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!

如果本篇博客有任何错误,请批评指教,不胜感激 !

要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!

相关推荐

订蛋糕在哪个软件
铁树开花是什么生肖
被下载流氓软件了且删除不了该怎么办