Thread之九:stop

搞過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方法釋放鎖是在第二點的,經過拋出異常來釋放鎖,經過證實,這種方式是不安全的,不可靠的。

相關文章
相關標籤/搜索