Java多线程初探——正确停止线程

来源:互联网 发布:安卓4.0淘宝 编辑:程序博客网 时间:2024/06/07 04:52

一、错误的方法——stop()方法

在1.0版本的jdk中,提供了一个stop方法来停止线程,但是这个方法现在已经被废弃了,因为使用这个方法停止线程,将会使线程戛然而止,我们甚至不知道程序执行到了哪里,资源是否已经释放,会出现一些不可预料的结果。

使用stop()方法停止线程实例:

定义一个线程类YieldRunnable.java

public class YieldRunnable implements Runnable{public volatile boolean isRunning = true;@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "开始执行!");while(isRunning) {for(int i = 1; i < 6; i++) {System.out.println(name + "执行了[" + i + "]次");//注意,yield是静态方法Thread.yield();}}System.out.println(name + "执行结束!");}}
接下来是main方法

public static void main(String[] args) {System.out.println("主线程开始执行!");YieldRunnable runnable = new YieldRunnable();Thread thread = new Thread(runnable, "线程1");thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread.stop();System.out.println("主线程执行结束!");}
截取部分执行结果片段:

……

线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
线程1执行了[1]次线程1执行了[1]次主线程执行结束!

从执行结果可以看出,在调用了stop方法之后,线程中的循环语句还没有执行结束线程就戛然而止了,甚至连最后执行结束的输出都没有打印出来,我们甚至不知道程序执行到哪里了,所以stop()方法是不推荐使用的。

二、正确的方法——使用中断信号

所谓使用中断信号,就是定义一个布尔型的标志位,用于标识线程是否继续执行,线程不定期检查该标志位,当需要将线程停止时,将标志位修改为中断,那么线程就可以被正确停止了。

使用中断信号停止线程实例:

YieldRunnable.java与上例相同,不做修改,写一个main方法

public static void main(String[] args) {System.out.println("主线程开始执行!");YieldRunnable runnable = new YieldRunnable();Thread thread = new Thread(runnable, "线程1");thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}runnable.isRunning = false;System.out.println("主线程执行结束!");}

上面的程序将YieldRunnable类中的isRunning设为中断信号,刚开始主线程运行,启动了子线程,之后主线程将子线程的中断信号设为false,子线程读取到中断信号之后就结束线程。
运行结果:

……

线程1执行了[4]次
线程1执行了[5]次
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
线程1执行结束!

由运行结果可以发现,子线程正确执行了所有的循环,并打印退出信息正确退出了。


三、另一种值得推敲的方法,interrupt()方法

在Java中还提供了一个方法,interrupt()方法,在jdk文档中这样描述:中断线程,这个方法真的能将线程停止吗?现在用一个实例尝试一下。
先写一个线程YieldRunnable.java
public class YieldRunnable implements Runnable{@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "开始执行!");while(true) {for(int i = 1; i < 6; i++) {System.out.println(name + "执行了[" + i + "]次");//注意,yield是静态方法Thread.yield();}long time = System.currentTimeMillis();while(System.currentTimeMillis() - time < 1000) {            /*             * 使用while循环模拟 sleep 方法,这里不要使用sleep,具体原因下面再说             */}}}}
再写一个main方法
public static void main(String[] args) {System.out.println("主线程开始执行!");YieldRunnable runnable = new YieldRunnable();Thread thread = new Thread(runnable, "线程1");thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt();System.out.println("主线程执行结束!");}
运行之后会发现,虽然执行了interrupt()方法,但是子线程并没有停下来,反而是一直执行下去,由此可以得知,interrupt()方法单独使用不能停止线程。那该如何使用interrupt()方法让它能够来停止线程呢?查阅了jdk之后,我们发现线程保有一个状态叫做中断状态,在调用interrupt()之后,线程的中断状态为true,Java提供两种方法获取线程的中断状态:
public static boolean interrupted();public boolean isInterrupted()
其中,interrupted()方法是静态的,同时调用了interrupted()方法之后线程的中断状态由该方法清除,换句话说,如果连续两次调用该方法,则第二次调用将返回 false。isInterrupted()方法则不会清除线程的中断状态。
现在修改上面的示例代码:
YieldRunnable.java
public class YieldRunnable implements Runnable{@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "开始执行!");while(!Thread.currentThread().isInterrupted()) {for(int i = 1; i < 6; i++) {System.out.println(name + "执行了[" + i + "]次");//注意,yield是静态方法Thread.yield();}long time = System.currentTimeMillis();while(System.currentTimeMillis() - time < 1000) {/** 使用while循环模拟 sleep 方法,这里不要使用sleep,具体原因下面再说*/}}System.out.println(name + "执行结束!");}}
执行结果:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
线程1执行结束!

从执行结果可以发现线程正确停止了,其实从代码中可以发现,要想使线程停止,其实还是判断的线程的中断状态,检查的是线程isInterrupted()方法的值,这其实也是一种中断信号。
在上面的线程代码中有一段循环,while(System.currentTimeMillis() - time < 1000),这个方法是模拟Thread的sleep(long l)方法的,那为什么这里不使用sleep()方法呢?我们从jdk文档里可以看到:当线程调用了interrupt()方法后,如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。也就是说线程不仅不会被中断,系统还会抛出一个异常。尝试修改一下线程的代码:
public class YieldRunnable implements Runnable{@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "开始执行!");while(!Thread.currentThread().isInterrupted()) {for(int i = 1; i < 6; i++) {System.out.println(name + "执行了[" + i + "]次");//注意,yield是静态方法Thread.yield();}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(name + "执行结束!");}}
执行结果:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
线程1执行了[1]次java.lang.InterruptedException: sleep interrupted


主线程执行结束!
线程1执行了[2]次
线程1执行了[3]次
……

不仅线程没有结束,在执行sleep()方法的时候还抛出了一个异常。那么对于这种情形下该如何处理才能使线程正确停止呢?一种可行的方法是在sleep()方法抛出异常时,在处理异常的时候再调用interrupt()方法将线程中止。代码如下:
public class YieldRunnable implements Runnable{@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "开始执行!");while(!Thread.currentThread().isInterrupted()) {for(int i = 1; i < 6; i++) {System.out.println(name + "执行了[" + i + "]次");//注意,yield是静态方法Thread.yield();}try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}System.out.println(name + "执行结束!");}}
运行结果:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
线程1执行结束!

从运行结果可以看到,线程就被正确停止了。




0 0
原创粉丝点击