多線程(四) 如何中止線程 多線程(四) 如何中止線程

  在Thread類中提供了能夠中止線程的方法(包括殺死和掛起):html

    @Deprecated
    public final void stop(){}

    @Deprecated
    public final void suspend(){}
    

   stop 和 suspend 添加的有Deprecated註釋,也便是該方法已經廢棄使用。那麼爲何會不建議使用這兩種方法呢?還有沒有其餘中止線程的方法?安全

一、stop()會當即殺死線程,沒法保證原子操做可以順利完成,存在數據錯誤的風險。不管線程執行到哪裏、是否加鎖,stop會當即中止線程運行,直接退出。多線程

   以下代碼:ide

 int account01 = 10;
    int account02= 0;
    Object lock = new Object();
    
    public void testStop() {
        class StopRunnable implements Runnable {
            @Override
            public void run() {
                //要求 account01 + account02 =10  恆成立
                while (true) {
                    synchronized (lock) {//加鎖保證操做的原子性
                        account01--;
                        sleep(200);//睡眠模擬執行過程
                        account02++;
                    }
                }
            }
        }
        Thread thread = new Thread(new StopRunnable());
        thread.start();
        sleep(1300);
        thread.stop();
        System.out.println("account01: " + account01 + "\naccount02: " + account02);
    }
    private void sleep(int time){ try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } }

 運行結果以下:post

account01: 3
account02: 6

 很明顯沒有保證二者的和爲10。說明在線程循環過程當中,最後一個加鎖循環體沒有完整執行結束,數據發生錯誤。除此以外,在執行stop方法以後,線程會當即釋放鎖,這一樣也會致使原子操做失敗數據異常。url

官方註釋:spa

Forces the thread to stop executing.
It is permitted to stop a thread that has not yet been started.
If the thread is eventually started, it immediately terminates.

 二、suspend()並未殺死線程,只是把線程掛起,中止線程的運行。但掛起以後並不會釋放鎖,這樣,若是有其它多個線程在等待該鎖,則程序將會發生死鎖。線程

  以下代碼:code

    int account01 = 10;
    int account02= 0;
    Object lock = new Object();

    public void testStop() {
        class StopRunnable implements Runnable {
            @Override
            public void run() {
                //要求 account01 + account02 =10  恆成立
                for(int i =0;i<5;i++){
                    synchronized (lock) {//加鎖保證操做的原子性
                        account01--;
                        System.out.println("....."+Thread.currentThread().getName());//爲了看到線程中止添加輸出線程名稱操做
                        sleep(200);//睡眠200ms
                        account02++;
                    }
                }
            }
        }
        Thread thread01 = new Thread(new StopRunnable());
        thread01.setName("thread01");
        Thread thread02 = new Thread(new StopRunnable());
        thread02.setName("thread02");

        thread01.start();
        thread02.start();

        sleep(500);
        thread01.suspend();
        while (true){
            sleep(1000);
            System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive());
        }
    }

運行結果以下:  htm

.....thread01
.....thread01
.....thread01
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true

由結果能夠看出,thread01一直在運行,而thread02一次也沒有執行到run方法。而後在執行thread01.suspend()以後,兩個線程都中止了運行run方法。但同時兩個線程都沒有死亡。

在代碼中只對thread01執行了suspend,那麼若是thread02獲取到鎖則應該繼續由thread02執行run方法,但並無,說明鎖lock一直由thread01持有,在掛起以後並未釋放。

其實在使用suspend()方法的時候是須要配合resume()同時使用的。

     ....
     ....
sleep(
500); thread01.suspend(); int time = 0;//添加time計數,在循環三次以後釋放 while (true){ time ++; if(time ==3){ thread01.resume();//釋放線程 } sleep(1000); System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive()); }

 執行結果以下:  

.....thread01
.....thread01
.....thread01
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true .....thread01 //釋放以後繼續運行 .....thread01 .....thread02 //thread01釋放鎖,thread02獲取鎖繼續運行 .....thread02
.....thread02
account01: 2 account02: 7 thread01 isAlive:false thread02 isAlive:true  //thread01 死亡,thread02活着
.....thread02
.....thread02
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false

 能夠看出,thread01.resume()以後thread01繼續運行,而後運行結束釋放鎖以後,thread02接着運行起來,這時能夠看到thread01已經死亡,而thread02依舊活着。直至兩個線程所有結束。若是正常使用suspend()和resume()並不會出現太大問題,只是在涉及到鎖的時候久須要格外當心了。r若是沒有使用到鎖,那麼其中一個線程的掛起並不會影響到其餘線程的執行。

對於public void interrupt(){}方法,該方法只是對阻塞狀態的線程(seleep、wait、join和IO/NIO操做)進行中斷操做,在調用interrupt方法的時候,若是線程處於阻塞狀態則會拋出InterruptedException/ClosedByInterruptException。在程序中只需對異常進行捕獲,並不影響後續的操做。對於未處於阻塞狀態的線程,調用interrupt方法沒有任何影響。因此interrupt嚴格意義上說並不屬於中止線程的方法。

那麼,到底該如何安全的中止線程呢?

  遵循的規則:讓線程本身中止本身

     兩種方法:一、線程任務執行完成,順利結束退出。二、設置終止標誌位,在循環的時候進行終止標誌位檢測,若是設置爲終止狀態則return結束線程。

 例如:一、線程執行完成自動退出:

            public void run() {
                for(int i = 0;i<10;i++){//循環十次以後run方法結束自動結束線程
                    System.out.println(Thread.currentThread().getName());
                }
            }

 二、設置終止標誌位。

    boolean isStop = false;//終止標誌位 當須要結束線程時,更改成true
    public void testInterrupt(){
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    if(isStop){//當須要結束線程的時候終止線程
                        //doSomething  進行一些收尾工做
                        return;
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            }
        });

  設置終止標誌位的時候線程不會當即終止,只有當循環到標誌位判斷的時候纔會執行退出操做,這樣就能夠在循環體中合適的地方執行退出邏輯,能夠保證原子操做的順利完成以及鎖的釋放。

 對於ExecutorService的void shutdown();方法,該方法只是中止線程池接受新的任務同時等待已提交線程結束,並不會中止線程。因此該方法不屬於中止線程的方法。

 

=========================================

 

原文連接:多線程(四) 如何中止線程 轉載請註明出處!

 

=========================================

 

---end

相關文章
相關標籤/搜索