【Java多线程】——停止线程

停止线程是在进行多线程开发中的重要的知识点,停止线程并不像break那样简单干脆,需要一些技巧性的处理。

本文将讨论如何在一个线程进行的过程中将其停止,停止一个线程意味着在当前线程执行完任务之前停止该线程,也就是放弃该线程正在进行的任务。虽然这看起来很简单,但是要做好防范工作,以避免可能会发生的错误。

如果停止一个线程可以用Thread.stop()方法,但是不建议使用,虽然它可以停止一个线程,但是他是不安全(unsafe)的,会造成一些我们不想看到的后果(关于stop()方法会在后面详细说明),而且这是一个已经被弃用的方法,在未来的java版本中,他可能将会不可用。

这里我们要使用到的是Thread.interrupt()方法,此方法并不能真正的将线程停止,只是为线程设置一个中断标记,我们可以通过Thread.interrupted()和Thread.isInterrupted()方法来查看线程是否处于中断状态,如果返回true,处于中断状态,接下来加入相应的判断操作以完成线程的中断。

1、判断线程是否是停止状态

在对线程进行停止操作之前,首先要判断线程是否处于要停止的状态,Java中提供了两个方法来判断这个状态:

1)this.interrupted():测试当前线程是否已经中断;

2)this.isInterrupted():测试线程是否已经中断;

从上面描述中应该已经能看出来两个方法的不同之处了吧,我们再来看一下两个方法是如何声明的:

    public static boolean interrupted() {

        return currentThread().isInterrupted(true);

    }

    public boolean isInterrupted() {

        return isInterrupted(false);

    }

可以看到,interrupted()是一个静态方法,返回当前线程的停止状态;isInterrputed()方法返回的是调用此方法的线程的停止状态。还有一个不同点是,当两次调用interrupted()时,如果第一次返回true,第二次会返回false,这是因为该方法每次被调用的时候会自动清除状态标记,将状态置为默认值false,而isInterrupted()则不会清除标记。

并且只有当线程还未执行完毕时调用isInterrupted()方法才能看到此线程的停止状态,如果线程已经执行完毕,就算使用interrupt()给线程使用了停止标记,使用isInterrupted()查看仍会返回false。

2、停止线程的方法——异常法

我们可以在for循环中增加对于线程停止状态的判断,如果是停止状态,则下面代码不再执行。

创建一个Mythread类

public class Mythread extends Thread{
	@Override
	public void run() {
		super.run();
		try {
			for(int i=0; i<500000; i++) {
				if(this.interrupted()) {
					System.out.println("停止状态,退出!");
					throw new InterruptedException();
				}
				System.out.println("i="+(i+1));
			}
		}
		catch(InterruptedException e) {
			System.out.println("捕获异常,停止线程");
			e.printStackTrace();
		}
	}
}

Run类包含一个main方法

public class Run {
	public static void main(String args[]) {
		try {
			Mythread mt = new Mythread();
		    mt.start();
			Thread.sleep(1000);
			mt.interrupt();
		} catch (InterruptedException e) {
			System.out.println("main catched");
			e.printStackTrace();
		}
		System.out.println("end!");
	}
}

在Mythread类的run()方法中,增加对该线程的停止状态的判断,如果处于停止状态,则输出退出,然后抛出一个InterruptedException异常,再进行捕获和相对应的处理,这样可以保证for循环后面的代码也不会继续执行,保证整个线程停止下来。运行结果如下所示:

/*

i=325813

i=325814

i=325815

i=325816

i=325817

end!

停止状态,退出!

捕获异常,停止线程

java.lang.InterruptedException

at Practice.Mythread.run(Mythread.java:12)

*/

3、在沉睡中停止线程

如果线程在sleep()的状态下被终止,会是什么情况呢?可以理解成一个人在睡觉的时候被杀害了,那么这个人睡觉之后的行为便不会再进行,比如说他不会再醒过来。

这里继续创建Mythread类

[java]  view plain  copy
  1. public class Mythread extends Thread{  
  2.     @Override  
  3.     public void run() {  
  4.         super.run();  
  5.         try {  
  6.             System.out.println("我开始睡觉啦!");  
  7.             Thread.sleep(200000);  
  8.             System.out.println("我醒来啦!");  
  9.         }  
  10.         catch(InterruptedException e) {  
  11.             System.out.println("在沉睡中被杀掉了!没法醒过来了!"+this.isInterrupted());  
  12.             e.printStackTrace();  
  13.         }  
  14.     }  
  15. }  

Run中包含一个main方法

[java]  view plain  copy
  1. public class Run {  
  2.     public static void main(String args[]) {  
  3.         try {  
  4.             Mythread mt = new Mythread();  
  5.             mt.start();  
  6.             Thread.sleep(1000);  
  7.             mt.interrupt();  
  8.         } catch (InterruptedException e) {  
  9.             e.printStackTrace();  
  10.         }  
  11.     }  
  12. }  

运行结果

/*

我开始睡觉啦!

在沉睡中被杀掉了!没法醒过来了!false

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at Practice.Mythread.run(Mythread.java:9)

*/

从打印结果来看,如果在sleep的状态下终止某一个线程,会进入catch语句,并且将他的终止状态清除,设为false。

另一种情况,如果是在 sleep()之前就遇到了interrupt()的话一样会直接进入catch语句,并结束线程。

4、使用return停止线程

将方法interrupt()与return结合起来也能终止线程。修改Mythread类与main方法如下

[java]  view plain  copy
  1. public class Mythread extends Thread{  
  2.     @Override  
  3.     public void run() {  
  4.         while(true) {  
  5.             if(this.isInterrupted()){  
  6.                 System.out.println("停止!");  
  7.                 return;  
  8.             }  
  9.             System.out.println("计时:"+System.currentTimeMillis());  
  10.         }  
  11.     }  
  12. }  
[java]  view plain  copy
  1. public class Run {  
  2.     public static void main(String args[]) throws InterruptedException {  
  3.         Mythread mt = new Mythread();  
  4.         mt.start();  
  5.         Thread.sleep(2000);  
  6.             mt.interrupt();  
  7.     }  
  8. }  

在检查到线程状态为终止的时候,不再输出时间,返回结果

运行如下:

/*

计时:1525424532412

计时:1525424532412

计时:1525424532412

计时:1525424532412

计时:1525424532412

计时:1525424532412

停止!

*/

虽然可以使用interrput()与return结合的方式来终止线程,但是还是推荐使用抛异常的方式终止线程,因为跑出的异常可以继续向上层传递,使得线程终止这一事件得以传播。

上文所说的Thread中的方法stop(),虽然可以用来使线程终止,但是使用此方法是十分暴力的,会造成一些不安全的后果。调用stop()方法后程序会抛出java.lang.ThreadDeath异常,但在通常情况下,此异常不需要显示的捕捉。

在java中stop()方法已经被作废,这是因为如果强制让线程停止,可能会是一些清理工作得不到完成,另外一个情况就是对于锁定的对象进行了解锁,导致数据得不到同步处理,会出现数据不一致的问题。

使用stop释放锁会造成数据不一致的后果,如果出现这种结果,程序处理数据就有可能遭到破坏,最终导致执行流程错误,所以一定要特别注意。

下面来看一个例子,创建类synObject如下:

[java]  view plain  copy
  1. public class synObject {  
  2.     private String username = "a";  
  3.     private String password = "aa";  
  4.     public String getUsername() {  
  5.         return username;  
  6.     }  
  7.     public void setUsername(String username) {  
  8.         this.username = username;  
  9.     }  
  10.     public String getPassword() {  
  11.         return password;  
  12.     }  
  13.     public void setPassword(String password) {  
  14.         this.password = password;  
  15.     }  
  16.     synchronized public void init(String username, String password) {  
  17.         try {  
  18.             this.username = username;  
  19.             Thread.sleep(200000);  
  20.             this.password = password;  
  21.         }  
  22.         catch(InterruptedException e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26. }  

其中username和password初始化为a和aa

Mythread和Run类如下:

[java]  view plain  copy
  1. public class Mythread extends Thread{  
  2.     private synObject object;  
  3.     public Mythread(synObject object) {  
  4.         super();  
  5.         this.object = object;  
  6.     }  
  7.     @Override  
  8.     public void run() {  
  9.         object.init("b""bb");  
  10.     }  
  11. }  
[java]  view plain  copy
  1. public class Run {  
  2.     public static void main(String args[]) throws InterruptedException {  
  3.         try {  
  4.             synObject object = new synObject();  
  5.             Mythread mt = new Mythread(object);  
  6.             mt.start();  
  7.             Thread.sleep(200);  
  8.             mt.stop();  
  9.             System.out.println(object.getUsername()+  
  10.                     " "+object.getPassword());  
  11.         }  
  12.         catch(InterruptedException e) {  
  13.             e.printStackTrace();  
  14.         }  
  15.     }  
  16. }  

运行后结果为:

/*

b aa

*/

可以看到在init方法执行的过程中,线程被强制stop(),所以被迫释放了锁,而被操作的对象object出现了数据不一致的结果。显然stop()方法在功能上是有缺陷的,所以不建议使用此方法。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值