如何停止一个线程?

停止线程在java语言中并不像break语句那样干脆,还需要一些技巧性的处理。

停止一个线程意味着在线程处理完任务之前停止正在做的操作,也就是放弃当前的操作,虽然这看起来非常简单,但是必须做好防范措施,以便达到预期的效果。

停止一个线程可以使用Thread.stop()方法,但不推荐使用此方法,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是被弃用作废的。

大多数情况下,停止一个线程使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

在java中有3种方法可以使正在运行的线程终止运行:

  • 使用退出标志使线程正常退出。
  • 使用stop()方法强行终止线程,但是这个方法不推荐使用,因为stop()和suspend()、resume()一样,都是作废过期的方法,使用它们可能发生不可预料的结果。
  • 使用interrupt()方法中断线程。

停止不了的线程

本示例将调用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()方法并没有将线程停止,那如何停止线程呢?

判断线程是否为停止状态

interrupted()方法

Thread.java类提供了两个判断方法来判断线程的状态是否是停止的:

  • public static boolean interrupted():测试currentThread()是否已经中断。
  • public boolean this.isInterrupted():测试this关键字所在类的对象是否已经中断。

那这两个方法有什么区别呢?先来看方法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()方法在官方文档的解释为:

判断当前线程是否已经中断。线程的中断状态由该方法清除。

isInterrupted()方法

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。

综上:

  • interrupted():测试当前线程是否已经是中断状态,执行后具有清除状态标志值的功能。
  • isInterrupted():测试线程Thread对象是否已经是中断状态,不清除状态标志。

能停止的线程-异常法

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()方法中断线程。

在sleep状态下停止线程

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()方法碰到一起就会出现异常:

  • 在sleep状态执行interrupt()方法会出现异常。
  • 调用interrupt()方法给线程打了中断标记,再执行sleep()方法也会出现异常。

使用return语句停止线程的缺点和解决方案

虽然使用return较“抛异常”法在代码结构上可以更加方便地实现线程的停止,不过还是建议使用“抛异常”法,因为在catch块中可以对异常的信息进行统一的处理。

你可能感兴趣的:(多线程编程核心技术,java,前端,tomcat)