Java中的线程Thread方法之—stop()

Java技术篇 尼古拉斯.赵四 6969℃ 0评论

搞过Java线程的人都知道,stop这个方法是臭名昭著了,早就被弃用了,但是现在任然有很多钟情与他的人,永远都放不下他,因为从他的字面意思上我们可以知道他貌似可以停止一个线程,这个需求是每个搞线程开发的人都想要的操作,但是他并非是真正意义上的停止线程,而且停止线程还会引来一些其他的麻烦事,下面就来详细的介绍一下这个方法的历史:

从SUN的官方文档可以得知,调用Thread.stop()方法是不安全的,这是因为当调用Thread.stop()方法时,会发生下面两件事:

1. 即刻抛出ThreadDeath异常,在线程的run()方法内,任何一点都有可能抛出ThreadDeath Error,包括在catch或finally语句中。

2. 会释放该线程所持有的所有的锁,而这种释放是不可控制的,非预期的。

 

当线程抛出ThreadDeath异常时,会导致该线程的run()方法突然返回来达到停止该线程的目的。ThreadDetath异常可以在该线程run()方法的任意一个执行点抛出。但是,线程的stop()方法一经调用线程的run()方法就会即刻返回吗?

  1. package com.threadstop.demo;  
  2.   
  3. public class ThreadStopTest {  
  4.     public static void main(String[] args) {    
  5.         try {    
  6.             Thread t = new Thread() {    
  7.                 //对于方法进行了同步操作,锁对象就是线程本身  
  8.                 public synchronized void run() {    
  9.                     try {    
  10.                         long start=System.currentTimeMillis();    
  11.                         //开始计数  
  12.                         for (int i = 0; i < 100000; i++)    
  13.                             System.out.println(“runing..” + i);    
  14.                         System.out.println((System.currentTimeMillis()-start)+“ms”);    
  15.                     } catch (Throwable ex) {    
  16.                         System.out.println(“Caught in run: “ + ex);    
  17.                         ex.printStackTrace();    
  18.                     }    
  19.                 }    
  20.             };    
  21.             //开始计数  
  22.             t.start();  
  23.             //主线程休眠100ms  
  24.             Thread.sleep(100);    
  25.             //停止线程的运行  
  26.             t.stop();  
  27.         } catch (Throwable t) {    
  28.             System.out.println(“Caught in main: “ + t);    
  29.             t.printStackTrace();    
  30.         }    
  31.     
  32.     }    
  33. }  

运行结果如下:

由于打印的数据太多了,就没有全部截图了,但是我们可以看到,调用了stop方法之后,线程并没有停止,而是将run方法执行完。那这个就诡异了,多次运行之后发现每次运行的结果都表明,工作线程并没有停止,而是每次都成功的数完数(执行完run方法),然后正常中止,而不是由stop()方法进行终止的。这个是为什么呢?根据SUN的文档,原则上只要一调用thread.stop()方法,那么线程就会立即停止,并抛出ThreadDeath error,查看了Thread的源代码后才发现,原先Thread.stop(Throwable obj)方法是同步的,而我们工作线程的run()方法也是同步,那么这样会导致主线程和工作线程共同争用同一个锁(工作线程对象本身),由于工作线程在启动后就先获得了锁,所以无论如何,当主线程在调用t.stop()时,它必须要等到工作线程的run()方法执行结束后才能进行,结果导致了上述奇怪的现象。

下面看一下stop的源码:

  1. @Deprecated  
  2.     public final void stop() {  
  3.         stop(new ThreadDeath());  
  4.     }  

再进到stop看:

  1. @Deprecated  
  2.     public final synchronized void stop(Throwable obj) {  
  3.         if (obj == null)  
  4.             throw new NullPointerException();  
  5.   
  6.         SecurityManager security = System.getSecurityManager();  
  7.         if (security != null) {  
  8.             checkAccess();  
  9.             if ((this != Thread.currentThread()) ||  
  10.                 (!(obj instanceof ThreadDeath))) {  
  11.                 security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);  
  12.             }  
  13.         }  
  14.         // A zero status value corresponds to “NEW”, it can’t change to  
  15.         // not-NEW because we hold the lock.  
  16.         if (threadStatus != 0) {  
  17.             resume(); // Wake up thread if it was suspended; no-op otherwise  
  18.         }  
  19.   
  20.         // The VM can handle all thread states  
  21.         stop0(obj);  
  22.     }  

stop0(obj)是一个native方法,我们看到stop方法是同步的,而这个同步的锁对象正好也是线程本身,所以造成上面的现象。

把上述工作线程的run()方法的同步去掉,再进行执行,结果就如上述第一点描述的那样了,运行结果如下:

从结果中我们可以看到,调用stop方法会抛出一个ThreadDeath异常,这时候run方法也就执行结束了,线程就终止了,这种是用抛异常来结束线程的,但是这种抛出线程是不安全的,因为他不可控制,不知道到在run方法中的何处就可能抛出异常,所以是危险的。下面在看一下stop的这个隐患可能造成的影响:

接下来是看看当调用thread.stop()时,被停止的线程会不会释放其所持有的锁,看如下代码:

  1. public static void main(String[] args) {    
  2. <span style=“white-space:pre”>  </span>//定义锁对象  
  3.         final Object lock = new Object();   
  4.         //定义第一个线程,首先该线程拿到锁,而后等待3s,之后释放锁  
  5.         try {    
  6.             Thread t0 = new Thread() {    
  7.                 public void run() {    
  8.                     try {    
  9.                         synchronized (lock) {    
  10.                             System.out.println(“thread->” + getName()  + ” acquire lock.”);    
  11.                             sleep(3*1000);  
  12.                             System.out.println(“thread->” + getName() + ” 等待3s”);  
  13.                             System.out.println(“thread->” + getName()  + ” release lock.”);    
  14.                         }    
  15.                     } catch (Throwable ex) {    
  16.                         System.out.println(“Caught in run: “ + ex);    
  17.                         ex.printStackTrace();    
  18.                     }    
  19.                 }    
  20.             };    
  21.     
  22.             //定义第二个线程,等待拿到锁对象  
  23.             Thread t1 = new Thread() {    
  24.                 public void run() {    
  25.                     synchronized (lock) {    
  26.                         System.out.println(“thread->” + getName()  + ” acquire lock.”);    
  27.                     }    
  28.                 }    
  29.             };    
  30.               
  31.             //线程一先运行,先拿到lock  
  32.             t0.start();    
  33.             //而后主线程等待100ms,为了做延迟  
  34.             Thread.sleep(100);    
  35.             //停止线程一  
  36.             //t0.stop();  
  37.             //这时候在开启线程二  
  38.             t1.start();    
  39.         } catch (Throwable t) {    
  40.             System.out.println(“Caught in main: “ + t);    
  41.             t.printStackTrace();    
  42.         }    
  43.     
  44.     }     

运行结果如下:

从运行结果中我们可以看到,当没有进行t0.stop()方法的调用时, 可以发现,两个线程争用锁的顺序是固定的。这个现象是正常的。

下面我们把t0.stop注释的哪行,删除注释,调用t0.stop()方法,运行结果如下:

 

从运行结果中我们可以看到,调用了t0.stop()方法后,可以发现,t0线程抛出了ThreadDeath error并且t0线程释放了它所占有的锁。

从上面的程序验证结果来看,thread.stop()确实是不安全的。它的不安全主要是:释放该线程所持有的所有的锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。

下面顺便说一下:

Java中多线程锁释放的条件:

1)执行完同步代码块,就会释放锁。(synchronized)
2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。(wait)

从上面的三点我就可以看到stop方法释放锁是在第二点的,通过抛出异常来释放锁,通过证明,这种方式是不安全的,不可靠的。

好吧,Thread中的stop方法就说到这里了,后续还会有关Thread中的一些方法的详解,一定要关注奥!

转载请注明:尼古拉斯.赵四 » Java中的线程Thread方法之—stop()

喜欢 (1)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址