搞過Java線程的人都知道,stop這個方法是臭名昭著了,早就被棄用了,可是如今任然有不少鍾情與他的人,永遠都放不下他,由於從他的字面意思上咱們能夠知道他貌似能夠中止一個線程,這個需求是每一個搞線程開發的人都想要的操做,可是他並不是是真正意義上的中止線程,並且中止線程還會引來一些其餘的麻煩事,下面就來詳細的介紹一下這個方法的歷史:java
從SUN的官方文檔能夠得知,調用Thread.stop()方法是不安全的,這是由於當調用Thread.stop()方法時,會發生下面兩件事:安全
1. 即刻拋出ThreadDeath異常,在線程的run()方法內,任何一點都有可能拋出ThreadDeath Error,包括在catch或finally語句中。多線程
2. 會釋放該線程所持有的全部的鎖,而這種釋放是不可控制的,非預期的。ui
當線程拋出ThreadDeath異常時,會致使該線程的run()方法忽然返回來達到中止該線程的目的。ThreadDetath異常能夠在該線程run()方法的任意一個執行點拋出。可是,線程的stop()方法一經調用線程的run()方法就會即刻返回嗎?this
package com.dxz.threadstop; public class ThreadStopTest { public static void main(String[] args) { try { Thread t = new Thread() { // 對於方法進行了同步操做,鎖對象就是線程自己 public synchronized void run() { try { long start = System.currentTimeMillis(); // 開始計數 for (int i = 0; i < 100000; i++) System.out.println("runing.." + i); System.out.println((System.currentTimeMillis() - start) + "ms"); } catch (Throwable ex) { System.out.println("Caught in run: " + ex); ex.printStackTrace(); } } }; // 開始計數 t.start(); // 主線程休眠100ms Thread.sleep(100); // 中止線程的運行 t.stop(); } catch (Throwable t) { System.out.println("Caught in main: " + t); t.printStackTrace(); } } }
運行結果以下:spa
因爲打印的數據太多了,就沒有所有截圖了,可是咱們能夠看到,調用了stop方法以後,線程並無中止,而是將run方法執行完。那這個就詭異了,屢次運行以後發現每次運行的結果都代表,工做線程並無中止,而是每次都成功的數完數(執行完run方法),而後正常停止,而不是由stop()方法進行終止的。這個是爲何呢?根據SUN的文檔,原則上只要一調用thread.stop()方法,那麼線程就會當即中止,並拋出ThreadDeath error,查看了Thread的源代碼後才發現,原先Thread.stop(Throwable obj)方法是同步的,而咱們工做線程的run()方法也是同步,那麼這樣會致使主線程和工做線程共同爭用同一個鎖(工做線程對象自己),因爲工做線程在啓動後就先得到了鎖,因此不管如何,當主線程在調用t.stop()時,它必需要等到工做線程的run()方法執行結束後才能進行,結果致使了上述奇怪的現象。.net
下面看一下stop的源碼1.8版本:線程
/** * @deprecated Method stop is deprecated */ public final void stop() { SecurityManager securitymanager = System.getSecurityManager(); if(securitymanager != null) { checkAccess(); if(this != currentThread()) securitymanager.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } if(threadStatus != 0) resume(); stop0(new ThreadDeath()); }
把上述工做線程的run()方法的同步去掉,再進行執行,結果就如上述第一點描述的那樣了,運行結果以下:code
從結果中咱們能夠看到,調用stop方法會拋出一個ThreadDeath異常,這時候run方法也就執行結束了,線程就終止了,這種是用拋異常來結束線程的,可是這種拋出線程是不安全的,由於他不可控制,不知道到在run方法中的何處就可能拋出異常,因此是危險的。下面在看一下stop的這個隱患可能形成的影響:對象
接下來是看看當調用thread.stop()時,被中止的線程會釋放其所持有的鎖,看以下代碼:
public static void main(String[] args) { // 定義鎖對象 final Object lock = new Object(); // 定義第一個線程,首先該線程拿到鎖,然後等待3s,以後釋放鎖 try { Thread t0 = new Thread() { public void run() { try { synchronized (lock) { System.out.println("thread->" + getName() + " acquire lock."); sleep(3 * 1000); System.out.println("thread->" + getName() + " 等待3s"); System.out.println("thread->" + getName() + " release lock."); } } catch (Throwable ex) { System.out.println("Caught in run: " + ex); ex.printStackTrace(); } } }; // 定義第二個線程,等待拿到鎖對象 Thread t1 = new Thread() { public void run() { synchronized (lock) { System.out.println("thread->" + getName() + " acquire lock."); } } }; // 線程一先運行,先拿到lock t0.start(); // 然後主線程等待100ms,爲了作延遲 Thread.sleep(100); // 中止線程一 // t0.stop(); // 這時候在開啓線程二 t1.start(); } catch (Throwable t) { System.out.println("Caught in main: " + t); t.printStackTrace(); } }
運行結果以下:
從運行結果中咱們能夠看到,當沒有進行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方法釋放鎖是在第二點的,經過拋出異常來釋放鎖,經過證實,這種方式是不安全的,不可靠的。