停止线程在java语言中并不像break语句那样干脆,还需要一些技巧性的处理。
停止一个线程意味着在线程处理完任务之前停止正在做的操作,也就是放弃当前的操作,虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效果。
停止一个线程可以使用Thread.stop()方法,但不推荐使用此方法,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是被弃用作废的。
大多数情况下,停止一个线程使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
在java中有3种方法可以使正在运行的线程终止运行:
本示例将调用interrupt()方法来停止线程,但interrupt()方法的使用效果并不像for+break语句那样,马上就停止循环。调用interrupt()方法仅仅是在当前线程中做了一个停止的标记,并不是真正停止线程。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
System.out.println("zzzzzzzzzzzzzzzzzz");
}
}
调用interrupt()方法并没有将线程停止,那如何停止线程呢?
Thread.java类提供了两个判断方法来判断线程的状态是否是停止的:
那这两个方法有什么区别呢?先来看方法this.isInterrupted()方法的解释:测试当前线程是否已经中断,当前线程是指运行this.interrupted()方法的线程。如下代码所示:
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(10);
thread.interrupt();
System.out.println("是否停止1?="+thread.interrupted());
System.out.println("是否停止2?="+thread.interrupted());
}
}
output:
i=517
i=518
i=519
i=520
i=521
是否停止1?=false
i=522
是否停止2?=false
i=523
i=524
i=525
i=526
i=527
虽然通过在thread对象上调用代码:
thread.interrupt()
然后又使用代码:
thread.interrupted()
判断线程是否中断,返回false,因为interrupted()方法返回当前线程是否中断,当前线程是main线程,从未中断过,所以返回false。
最好使用Thread.interrupted(),因为interrupted()为静态方法,大多数是针对currentThread()线程进行操作的。
public class Run2 {
public static void main(String[] args) {
Thread.currentThread().interrupt();
System.out.println("是否停止1?=" + Thread.interrupted());
System.out.println("是否停止2?=" + Thread.interrupted());
System.out.println("end!");
}
}
output:
是否停止1?=true
是否停止2?=false
end!
为啥第一个interrupted()方法判断了当前线程是否停止状态,第二个却返回false呢?
interrupted()方法在官方文档的解释为:
判断当前线程是否已经中断。线程的中断状态由该方法清除。
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(10);
thread.interrupt();
System.out.println("是否停止1?="+thread.isInterrupted());
System.out.println("是否停止2?="+thread.isInterrupted());
}
}
output:
i=542
i=543
i=544
i=545
i=546
i=547
是否停止1?=true
i=548
是否停止2?=true
i=549
i=550
i=551
i=552
从结果中可以看出,isInterrupted()方法并未清除状态标志,不具有此功能,所以输出两个true。
综上:
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
if(this.isInterrupted()){
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(10);
thread.interrupt();
System.out.println("end");
}
}
output:
i=59
i=60
i=61
i=62
i=63
i=64
i=65
i=66
i=67
i=68
i=69
i=70
i=71
i=72
i=73
已经是停止状态了!我要退出了!
end
上面代码虽然停止了线程,但如果for语句下面还有语句,那么程序还是会继续运行。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
if(this.isInterrupted()){
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,线程并未停止");
}
}
output:
i=32
i=33
i=34
i=35
i=36
i=37
i=38
i=39
i=40
i=41
i=42
end
已经是停止状态了!我要退出了!
我被输出,线程并未停止
如何解决语句继续执行的问题呢?使用异常,如下代码所示:
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 5000; i++) {
if(this.isInterrupted()){
System.out.println("已经是停止状态了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被输出,线程并未停止");
}catch (InterruptedException e){
System.out.println("线程中断,抛出异常,被捕获");
e.printStackTrace();
}
}
}
output:
i=94
i=95
i=96
i=97
java.lang.InterruptedException
i=98
end
已经是停止状态了!我要退出了!
at com.example.threadcore.MyThread.run(MyThread.java:16)
线程中断,抛出异常,被捕获
由程序运行结果可以看出,线程终于被正确停止了。这就是使用interrupt()方法中断线程。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止,进入catch" + this.isInterrupted());
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(10);
thread.interrupt();
System.out.println("end");
}
}
output:
run begin
end
在沉睡中被停止,进入catch false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.example.threadcore.MyThread.run(MyThread.java:14)
从运行结果来看,如果线程在sleep状态下停止,则该线程会进入catch语句,并且清除停止状态值,变成false。
以上示例是先调用sleep()方法,再调用interrupt()方法停止线程,还有一个反操作,即先调用interrupt()方法,再调用sleep()方法,这种情况下也会出现异常。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 10000; i++) {
System.out.println("i= " + (i + 1));
}
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep,进入catch " + this.isInterrupted());
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
thread.interrupt();
System.out.println("end");
}
}
output:
i= 9994
i= 9995
i= 9996
i= 9997
i= 9998
i= 9999
i= 10000
run begin
先停止,再遇到了sleep,进入catch false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.example.threadcore.MyThread.run(MyThread.java:17)
综上,不管调用的顺序,只要interrupt()和sleep()方法碰到一起就会出现异常:
虽然使用return较“抛异常”法在代码结构上可以更加方便地实现线程的停止,不过还是建议使用“抛异常”法,因为在catch块中可以对异常的信息进行统一的处理。