java线程之停止线程
在Java中有以下3种方法可以终止一个正在运行的线程:
1、使用退出标志,是线程正常退出,也就是run方法完成后线程终止。
2、使用stop方法强制终止线程,但不推荐使用这个方法,因为stop、suspend和resume一样,都是作废过期的方法。
3、使用interrupt方法中断线程,大多数停止一个线程使用Thread.interrupt()方法,但是这个方法不会终止一个正在运行的线程,还需要加入一些判断才可以完成线程的停止。
下面我就用几个例子来介绍一下各种停止线程的效果。
一、停不了的线程——interrupt
本例子将调用interrupt()方法来停止线程,但是效果并不理想,并不能停止正在运行的线程,因为调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不能真的停止线程。
public static void main(String[] args) {
MyThread1 t = new MyThread1();
t.start();
try {
Thread.sleep(2);
t.interrupted();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
}
public static class MyThread1 extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
System.out.println("i=" + (i + 1));
}
}
}
运行结果我值截取了最后一段,控制台上正好输出到5000,说明调用interrupt方法并没有停止线程,运行结果如下:
那么怎么才能停止线程呢?下面我回来介绍。
二、能停止的线程——异常法
先看一个例子:
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(50);
t.interrupt();
System.out.println("end");
}
public static class MyThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for (int i = 0; i < 5000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("如果我被输出了,说明run方法还在继续执行,线程并未停止!");
}
}
输出结果如下:
从上面的结果可以看出来,这样写只是把for循环结束了,但是下面接着输出了“如果我被输出了,说明run方法还在继续执行,线程并未停止!”这句话,说明这样写并不能让run方法停止,所以这样写不可行。下面再来看一个例子。
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(50);
t.interrupt();
System.out.println("end");
}
public static class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 5000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
//异常法结束线程
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("进入Thread线程的run方法中的catch!");
e.printStackTrace();
}
}
}
下面来看一下控制台上的输出结果:
来分析一下结果,throw new InterruptedException()这句话下面的内容已经不会再执行了,因为“我在for下面”这句话并没有输出来,而是进入了catch,结束了整个run方法。所以异常方法来停止线程方法是靠谱的。
三、在沉睡中停止线程
public static void main(String[] args) {
try {
MyThread t = new MyThread();
t.start();
Thread.sleep(200);
t.interrupt();
} catch (Exception e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
public static class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
//为了确保能停止线程,这个休眠时间尽量长一点,如果时间太短,有可能主线程还没休眠结束呢,子线程就休眠结束了,这个线程也就正常运行了。
sleep(2000);
System.out.println("run end");
} catch (Exception e) {
System.out.println("在沉睡中被停止!进去catch!" + this.isInterrupted());
e.printStackTrace();
}
}
}
运行结果如下:
来分析一下结果,"run end"这句话没有输出,说明子线程里的sleep下面的内容再也不会执行了,而是进入到catch里面了,从而停止了run方法。说明这样写也是靠谱的。
四、能停止的线程——暴力停止
使用stop()方法停止线程是非常暴力的行为,并且使用stop()方法会导致运行结果不一样,已经被官方文档废弃使用了。不使用的原因是不安全,假如当执行到stop方法时,子线程正在执行 synchronized里边的内容,那么不管
synchronized的方法有没有执行完,都会立刻停止这个线程,造成线程不安全。下面我来展示一个例子。
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try {
Thread.sleep(9000);
t.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
super.run();
while (true) {
i++;
System.out.println("i=" + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
五、使用return停止线程
将interrupt()和return结合也能实现停止线程的效果。但是建议使用抛异常停止线程,因为可以处理发生异常时候的相关信息。下面看一段代码。
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(2);
t.interrupt();
}
public static class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
return;
}
System.out.println("i=" + (i + 1));
}
}
}
下面看一下运行结果:
六、暂停线程
暂停线程异味着此线程还可以恢复运行,在java中使用suspend()方法暂停线程,使用resume()方法恢复线程。不推荐suspend()和resume()方法,是因为容易造成死锁,比如执行suspend()这个方法的线程持有一个重要资源的锁,那么在没有执行恢复方法之前,是不会释放掉这个锁的,但是这个时候有另一个线程执行了resume()方法,但是这个线程恢复以后要想继续执行下去需要那个重要资源的锁,这个时候两个线程就造成死锁了
使用下面看一段代码。
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
//A段 暂停线程
t.suspend();
System.out.println("A="+System.currentTimeMillis() +"\t"+"i="+t.getI());
Thread.sleep(500);
System.out.println("A="+System.currentTimeMillis() +"\t"+"i="+t.getI());
//B段 继续恢复
t.resume();
System.out.println("B="+System.currentTimeMillis() +"\t"+"i="+t.getI());
Thread.sleep(500);
System.out.println("B="+System.currentTimeMillis() +"\t"+"i="+t.getI());
//C段 暂停线程
t.suspend();
System.out.println("C="+System.currentTimeMillis() +"\t"+"i="+t.getI());
Thread.sleep(500);
System.out.println("C="+System.currentTimeMillis() + "\t"+"i="+t.getI());
}
public static class MyThread extends Thread {
private long i = 0;
public long getI(){
return i;
}
public void setI(long i){
this.i=i;
}
@Override
public void run() {
super.run();
while (true) {
i++;
}
}
}
下面来分析一下运行结果:在A段中,先执行的是suspend()暂停线程的方法,所以从结果可以看出,系统的当前时间在变,但是i值确一直是0,说明线程没有在执行,仍是初始化的值,到了B段,执行了resume()恢复线程的方法,从结果也可以看出,当前系统的时间在变,i值也在改变,说面线程已经被恢复并在执行了,到了C段,出现这样的结果和A段原因是一样的。
以上我说了很多小例子,如果亲手敲一遍代码,看一下执行结果,会有更好的理解,毕竟线程执行的结果不是单一的。