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()方法的值,这其实也是一种中断信号。线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
线程1执行结束!
在上面的线程代码中有一段循环,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]次
……
线程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执行结束!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
线程1执行结束!
从运行结果可以看到,线程就被正确停止了。
0 0
- Java多线程初探——正确停止线程
- 27、Java入门—多线程之线程的正确停止
- Java多线程学习笔记—线程停止
- Java多线程初探——守护线程
- java 多线程 停止线程
- JAVA-多线程-停止线程
- java多线程 ---- 停止线程
- Java多线程-停止线程
- 如何正确停止线程(JAVA)
- java基础——多线程——线程停止
- Java多线程3——如何停止线程
- java多线程学习笔记(八) ——线程停止(interrupt)
- Java多线程学习(1)——停止线程
- 1.7 JAVA多线程编程——停止线程
- java多线程-停止线程interrupt
- Java多线程 线程的停止
- java多线程之停止线程
- Java笔记 - 多线程 - 停止线程
- JAVA操作Excel文件
- UVA 10099 - The Tourist Guide
- Iterator
- 获取手机分辨率(屏幕大小)
- LeetCode No332. Reconstruct Itinerary
- Java多线程初探——正确停止线程
- hdu5652 India and China Origins(并查集联通)
- 解决InvalidDataAccessApiUsageException: Executing an update/delete query
- 2016.06.04 教学妹弄gephi遇到的一些问题
- XTU Binary Search Tree(LIS变形)
- linux网络协议栈内核分析
- 中间件的简要概述
- Tamura纹理特征的matlab实现
- c++关于函数的按值传递和按址传递