不使用stop方法停止线程,停止线程的最佳办法

线程启动完毕后,在运行时可能需要终止,Java提供的终止方法只有一个stop,但是我 不建议使用这个方法,因为它有以下三个问题:

(1)  stop方法是过时的

从Java编码规则来说,已经过时的方法不建议采用。

(2)  stop方法会导致代码逻辑不完整

stop方法是一种“恶意”的中断,一旦执行stop方法,即终止当前正在运行的线程,不 管线程逻辑是否完整,这是非常危险的。看如下的代码:

public static void main(String[] args) throws Exception {

//子线程

Thread thread = new Thread() {

  @Override

  public void run () {
     try {

     //子线程休眠1秒 Thread.sleep(1000);

     } catch (InterruptedException e) {

         //异常处理

    }

     System.out.printIn ("此处代码不会执行");

 }

};

//启动线程                                                       、

    thread.start();

  //主线程休眠0.1秒 
   Thread.sleep(100);
  //子线程停止
   thread.stop();

}


这段代码的逻辑是这样的:子线程是一个匿名内部类,它的run方法在执行时会休眠1 秒钟,然后再执行后续的逻辑,而主线程则是休眠0.1秒后终止子线程的运行,也就是说, JVM在执行thread.stop()时,子线程还在执行sleep(1000),此时stop方法会清除栈内信息, 结束该线程,这也就导致了 run方法的逻辑不完整,输出语句println代表的是一段逻辑,可 能非常重要,比如子线程的主逻辑、资源回收、情景初始化等,但是因为stop线程了,这些 就都不再执行,于是就产生了业务逻辑不完整的情况。

这是极度危险的,因为我们不知道子线程会在什么时候被终止,Stop连基本的逻辑完整 性都无法保证。而且此种操作也是非常隐蔽的,子线程执行到何处会被关闭很难定位,这为以后的维护带来了很多麻烦。

(3)  stop方法会破坏原子逻辑

多线程为了解决共享资源抢占的问题,使用了锁概念,避免资源不同步,但是正因此原因,stop方法却会带来更大的麻烦:它会丢弃所有的锁,导致原子逻辑受损。例如有这样一段程序:

public class SafeStopThread implements Runnable{
   int a=0;
   @Override
	public void run() {
		// TODO Auto-generated method stub
			   synchronized ("") {
					a++;
					try {
						Thread.sleep(100);
					} catch (Exception e) {
						// TODO: handle exception
					}
					a--;
					String tn=Thread.currentThread().getName();
					System.out.println(tn+":a="+a);
					
				}
	}
}


MultiThread实现了 Runnable接口,具备多线程能力,其中run方法中加上了 synchronized 代码块,表示内部是原子逻辑,它会先自增然后再自减少,按照synchronized同步代码块的规 则来处理,此时无论启动多少个线程,打印出来的结果都应该是a=0,但是如果有一个正在执 行的线程被stop,就会破坏这种原子逻辑,代码如下:

public static void main(String[] args) {
	   SafeStopThread t=new SafeStopThread();
	   Thread t1=new Thread(t);
	   t1.start();
	   for(int i=0;i<5;i++){
		/*   SafeStopThread s=new SafeStopThread();
		   new Thread(s).start();*/ // 不共享stop变量
		   
		   new Thread(t).start();//stop变量相同
	   }
	   
	 t1.stop();//a==1
}


首先要说明的是所有线程共享了一个SafeStopThread的实例变量t,其次由于在run方法中加入了同步代码块,所以只能有一个线程进入到synchronized块中。此段代码的

序如下:

1)  线程tl启动,并执行run方法,由于没有其他线程持同步代码块的锁,所以tl线程 执行自加后执行到sleep方法即开始休眠,此时a=l。

2)  JVM又启动了 5个线程,也同时运行run方法,由于synchronized关键字的阻塞作 用,这5个线程不能执行自增和自减操作,等待tl线程锁释放。

3)  主线程执行了 tl.stop方法,终止了 tl线程,注意,由于a变量是所有线程共享的, 所以其他5个线程获得的a变量也是1。

4)  其他5个线程依次获得CPU执行机会,打印出a值。

分析了这么多,相信读者也明白了输出的结果,结果如下:

Thread-5:a =1 Thread-4:a =1 Thread-3:a =1 Thread-2:a=1 Thread-1:a =1

原本期望synchronized同步代码块中的逻辑都是原子逻辑,不受外界线程的干扰,但 是结果却出现原子逻辑被破坏的情况,这也是stop方法被废弃的一个重要原因:破坏了原 子逻辑。

既然终止一个线程不能使用stop方法,那怎样才能终止一个正在运行的线程呢?答案也 很简单,使用自定义的标志位决定线程的执行情况,代码如下:

public class SafeStopThread implements Runnable{
   private volatile boolean stop=false;//此变量必须加上volatile
   int a=0;
   @Override
	public void run() {
		// TODO Auto-generated method stub
		while(!stop){
			   synchronized ("") {
					a++;
					try {
						Thread.sleep(100);
					} catch (Exception e) {
						// TODO: handle exception
					}
					a--;
					String tn=Thread.currentThread().getName();
					System.out.println(tn+":a="+a);
					
				}
		}
      //线程终止
     public void terminate(){
         stop=true;
      }
  public static void main(String[] args) {
       SafeStopThread t=new SafeStopThread();
       Thread t1=new Thread(t);
       t1.start();
       for(int i=0;i<5;i++){ 
           new Thread(t).start();
       }
     t.terminate();
   }

 }


 
 

这是很简单的办法,在线程体中判断是否需要停止运行,即可保证线程体的逻辑完整性,而且也不会破坏原子逻辑。可能有读者对Java API比较熟悉,于是提出疑问:Thread不 是还提供了 interrupt中断线程的方法吗?这个方法可不是过时方法,那可以使用吗?它可以 终止一个线程吗?

非常好的问题,interrupt,名字看上去很像是终止一个线程的方法,但是我可以很明确 地告诉你,它不是,它不能终止一个正在执行着的线程,它只是修改中断标志而已,例如下面一段代码:

public static void main(String[]args) {

    Thread tl - new Thread(){

      public void run()   {

        //线程一直运行 while (true) {

        System, out .printIn ( "Running... ");
       }
   };
   //启动tl线程 
    tl.start();

  //中断tl线程 
    tl.interrupt 0;

}

执行这段代码,你会发现一直有Running在输出,永远不会停止,似乎执行了 interrupt 没有任何变化,那是因为interrupt方法不能终止一个线程状态,它只会改变中断标志位(如 果在tl.intemipt()前后输出tl.islntemipted()则会发现分别输出了 false和true),如果需要终 止该线程,还需要自行进行判断,例如我们可以使用interrupt编写出更加简洁、安全的终止 线程代码:

class SafeStopThread extends Thread{

   @Override

   public void run() {

     //判断线程体是否运行 
     while(!islnterrupted()) { 
          II Do Something
      }
   }
}


总之,如果期望终止一个正在运行的线程,则不能使用已经过时的Stop方法,需要自行 编码实现,如此即可保证原子逻辑不被破坏,代码逻辑不会出现异常。当然,如果我们使用的是线程池(比如ThreadPoolExecutor类),那么可以通过shutdown方法逐步关闭池中的线 程,它采用的是比较温和、安全的关闭线程方法,完全不会产生类似stop方法的弊端。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值