多線程併發篇——如何中止線程

  筆者是廣州的java程序員,剛畢業半年,工做之餘寫博客,若是以爲個人文章寫得不錯,能夠關注個人微信公衆號(J2彬彬),裏面會有更多精彩內容。從2018年8月份開始寫博客,但願往後寫出更多通俗易懂的技術文章與你們一同分享。java

前言

  你有沒有想過,如何中止一個線程?不少人首先會想到Thread.stop()方法,可是這個方法已通過時,不推薦使用,由於這個方法會帶來安全問題,什麼安全問題呢?後面會有詳細說明。咱們先講講目前JDK API推薦使用中止線程的方法Thread.interrupt()方法。程序員

Thread.interrupt()

  既然不能直接stop線程,那麼只有一種方法可讓線程結束,那就是讓run方法運結束。   Thread.interrupt()表明的意思是「中止,停止」。可是這個方法須要加入一個判斷才能夠完成線程的中止。一旦檢測到線程處於中斷狀態,那麼就有機會結束run方法。   下面以一個代碼示例看看它是如何中止線程的?安全

1、interrupt()中止線程
package com.bingo.thread.stopThread;

/** * Created with IntelliJ IDEA. * Description: 中止線程不推薦使用stop方法,此方法不安全,咱們可使用Thread.interrupt() * User: bingo */
public class Run {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.interrupt();
        System.out.println("end...");
    }
}


class MyThread extends Thread{

    @Override
    public void run() {
        super.run();

        for (int i = 0; i < 10000 ; i++) {

            if(this.isInterrupted()){
                System.out.println("已是中止狀態了,我要退出了");
                break;
            }
            System.out.println("i="+(i+1));
        }
    }
}
複製代碼

運行結果:微信

i=1
......
i=3102
i=3103
i=3104
i=3105
i=3106
i=3107
i=3108
end...
已是中止狀態了,我要退出了
複製代碼

  運行結果咱們能夠看到,當myThread線程的循環運行到i=3108的時候,因爲線程被中斷,而跳出循環,這個例子很好詮釋了interrupt方法的做用。多線程

2、interrupt能夠清除線程的凍結狀態,讓線程恢復到可運行的狀態上來
package com.bingo.thread.stopThread;

/** * Created with IntelliJ IDEA. * Description: interrupt能夠清除線程的凍結狀態,讓線程恢復到可運行的狀態上來。 * User: bingo */
public class Run2 {

    public static void main(String[] args) {
        MyThread2 thread = new MyThread2();
        thread.start();
        thread.interrupt();
        System.out.println("main end...");
    }
}

class MyThread2 extends Thread{

    @Override
    public void run() {
        System.out.println("run begin...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            System.out.println("run 在沉睡中被停止,進入catch");
            e.printStackTrace();
        }
        System.out.println("run end...");
    }
}
複製代碼

運行結果:ide

main end...
run begin...
run 在沉睡中被停止,進入catch
run end...
java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at com.bingo.thread.stopThread.MyThread2.run(Run2.java:26)
複製代碼

  從運行結果咱們能夠看到,原本run方法睡眠時間爲1000秒,可是打印結果倒是瞬間的,其實sleep已經被interrupt方法給打斷,此時線程凍結狀態被清除,並拋出異常,被catch捕獲,打印異常信息。學習

暴力中止——Stop

  下圖是JDK API對stop方法的描述,能夠看到已過期,不推薦使用,並告訴咱們此方法爲什麼不安全? this

  若是某個線程加鎖,stop方法中止該線程時會把鎖釋放掉,可能形成數據不一致的狀況。下面代碼能夠說明此問題。

package com.bingo.thread.stopThread;

/** * Created with IntelliJ IDEA. * Description: stop()方法爲什麼不安全?下面例子可解答 * User: bingo */
public class StopTest {

    public static void main(String[] args) {

        try {

            SynchrionzedObject object = new SynchrionzedObject();
            MyThread3 t = new MyThread3(object);
            t.start();

            Thread.sleep(500);

            t.stop();

            System.out.println(object.getUsername()+" "+object.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class SynchrionzedObject{

    private String username = "a";

    private String password = "aa";

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public synchronized void printString(String username,String password){
        try {

            this.username = username;
            Thread.sleep(10000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

class MyThread3 extends Thread{

    private SynchrionzedObject object;

    public MyThread3(SynchrionzedObject object){
        this.object = object;
    }

    @Override
    public void run() {
        object.printString("b", "bb");
    }
}

複製代碼

運行結果:spa

b aa
複製代碼

  從上面例子咱們能夠看到雖然printString方法加了鎖,可是run方法運行過程當中忽然被stop了,鎖被釋放,MyThread線程只對username進行了賦值,而password賦值動做未執行,此時形成數據不一致。線程

最後

  其實java多線程不少方法內部都是native方法,也就是基於JVM內部實現的,因此咱們有必要結合JVM一塊兒學習這部分的內容。技術的進步須要每一個小小的積累,才能走得更遠,更長久。

相關文章
相關標籤/搜索