【多線程與并發(fā)】:線程的創(chuàng)建、狀態(tài)、方法

1、線程創(chuàng)建

創(chuàng)建線程有三種方式

  • 繼承Thread類
    ①定義Thread類的子類,并重寫該類的run方法,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執(zhí)行體。
    ②創(chuàng)建Thread子類的實例,即創(chuàng)建了線程對象。
    ③調(diào)用線程對象的start()方法來啟動該線程。

  • 實現(xiàn)Runnable接口
    ①定義runnable接口的實現(xiàn)類,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執(zhí)行體。
    ②創(chuàng)建 Runnable實現(xiàn)類的實例,并依此實例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象。
    ③調(diào)用線程對象的start()方法來啟動該線程。

  • 實現(xiàn)Callable接口(jdk1.5新增,在java.util.concurrent包)
    ①創(chuàng)建Callable接口的實現(xiàn)類,并實現(xiàn)call()方法,該call()方法將作為線程執(zhí)行體,并且有返回值。
    ②創(chuàng)建Callable實現(xiàn)類的實例,使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值。
    ③使用FutureTask對象作為Thread對象的target創(chuàng)建并啟動新線程。
    ④調(diào)用FutureTask對象的get()方法來獲得子線程執(zhí)行結束后的返回值

示例代碼

public class ThreadLearning {
    
//------------------------繼承Thread類-----------------------
    //匿名內(nèi)部類的形式
    @org.junit.Test
    public void generateThread_1() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                //do something
                System.out.println("1");
            }
        };
        thread.start();
    }

    //正常形式
    @org.junit.Test
    public void generateThread_2() {
        Thread1 thread1 = new Thread1();
        thread1.start();
    }

    class Thread1 extends Thread{
        @Override
        public void run() {
            //do something
            System.out.println("2");
        }
    }


//------------------------實現(xiàn)Runnable接口-----------------------
    //匿名內(nèi)部類的形式
    @org.junit.Test
    public void generateThread_3() {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //do something
                System.out.println("1");
            }
        });
        thread.start();
    }

    //正常形式
    @org.junit.Test
    public void generateThread_4() {
        Thread2 thread2 = new Thread2();
        Thread thread = new Thread(thread2);
        thread.start();
    }

    class Thread2 implements Runnable{
        @Override
        public void run() {
            //do something
            System.out.println("2");
        }
    }


//------------------------實現(xiàn)Callable接口-----------------------
    @org.junit.Test
    public void generateThread_5() {
        Thread3 thread3 = new Thread3();
        FutureTask<String> futureTask = new FutureTask<String>(thread3);
        new Thread(futureTask).start();
        try {
            String s = futureTask.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

    class Thread3 implements Callable<String>{
        @Override
        public String call() throws Exception {
            return "我是實現(xiàn)Callable接口的線程類";
        }
    }
}
2、線程狀態(tài)分類
/**
 * 一個線程在給定的某個時刻,只能處在下面的6中狀態(tài)中的一種。
 *
 * 這些狀態(tài)是相對于虛擬機而言的,并不能反映出任何操作系統(tǒng)中線程狀態(tài)。
 *
 */
public enum State {
    /**
     *
     * 新建狀態(tài)
     *
     * 創(chuàng)建后還沒有啟動的線程處在NEW的狀態(tài);
     *
     * 而啟動線程只有start()方法。也就是說還未調(diào)用start()方法的線程處在NEW的狀態(tài)
     */
    NEW,

    /**
     *
     * 運行狀態(tài)
     *
     * 處在此狀態(tài)的線程有可能正在運行,也有可能正在等待CPU為它分配執(zhí)行時間
     * 對于虛擬機而言,處在RUNNABLE狀態(tài)的線程就是正在執(zhí)行。
     *
     */
    RUNNABLE,

    /**
     *
     * 阻塞狀態(tài)
     *
     * 處在阻塞狀態(tài)的線程正在等待一個鎖
     * 
     * 在程序等待進入同步區(qū)域的時候,線程將進入這種狀態(tài)。
     * 
     *
     */
    BLOCKED,

    /**
     * 
     * 無限期等待狀態(tài)
     * 
     * 線程進入無限期等待狀態(tài)的原因是調(diào)用了下面三種方法之一:
     * ①沒有設置Timeout參數(shù)的Object.wait()方法
     * ②沒有設置Timeout參數(shù)的Thread.join()方法
     * ③LockSupport.park()方法
     * 
     * 
     */
    WAITING,
    /**
     * 
     * 限期等待狀態(tài)
     * 
     * 線程進入限期等待狀態(tài)的原因是調(diào)用了下面五種方法之一:
     * 
     * ①設置Timeout參數(shù)的Object.wait()方法
     * ②設置Timeout參數(shù)的Thread.join()方法
     * ③LockSupport.parkNanos()方法
     * ④LockSupport.parkUntil
     * ⑤Thread.sleep()方法
     * 
     */
    TIMED_WAITING,

    /**
     * 
     * 結束狀態(tài)
     * 
     * 線程已經(jīng)完成執(zhí)行。
     */
    TERMINATED;
}

3、狀態(tài)切換圖
線程狀態(tài)切換圖.png

4、各個方法的解釋

先看屬于線程Thread的方法

public static native void yield();

該方法向線程調(diào)度器提示,當前線程(調(diào)用該方法的線程對象代表的線程)愿意讓出處理器的使用權,但是線程調(diào)度器可能會忽視該提示。
很少有適合使用該方法的地方。它可能在調(diào)試或者測試的時候,幫助重現(xiàn)由于競爭條件出現(xiàn)的bug。它還可能在設計并發(fā)控制結構的時候提供幫助,比如java.util.concurrent.locks包中的一些類。

public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos) throws InterruptedException;

該方法會讓當前線程(調(diào)用該方法的線程對象代表的線程)睡眠指定的一段時間(暫時停止執(zhí)行),但當前線程不會失去鎖的擁有權(還會繼續(xù)占用鎖)。
上述兩個方法只是指定的睡眠時間精度不同。

public synchronized void start()

該方法會讓當前線程(調(diào)用該方法的線程對象代表的線程)開始執(zhí)行。JVM虛擬機會調(diào)用該線程的run()方法。對一個線程只能start()一次。

public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException
public final void join() throws InterruptedException

該方法會讓當前線程(調(diào)用該方法的線程,注意,這里并不是thread.join()中thread所表示的線程,而是方法thread.join()所在的線程)等待指定時間(如果為0,表示一直等到thread執(zhí)行完),讓join()方法的所屬線程thread執(zhí)行。
join()方法內(nèi)部是調(diào)用wait()方法實現(xiàn)的。

再看Object中的方法

public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
public final void wait() throws InterruptedException

該方法會讓當前線程(該方法調(diào)用所在的線程)進入等待狀態(tài),直到被該對象(wait()方法的this對象)的notify()或者notifyAll()喚醒,或者等待時間結束(wait(long timeout)中的timeout結束)。
當前線程必須擁有該對象的monitor。
當前線程會將自己放置在等待集中(該對象的等待集),并放棄所有對該對象的同步聲明。該線程就會不可用并保持休眠,直到發(fā)生下面的4種事件中的任意一件:
①其他線程觸發(fā)了該對象的notify方法,那么該線程就可能會被喚醒(并不一定會喚醒,可能很多線程都wait該對象);
②其他線程觸發(fā)了notifyAll方法;
③被其他線程中斷(interrupt()方法)
④設定的等待時間已到。如果是wait(0)或者wait()則會一直等待直到喚醒。

上述事件之一發(fā)生后,該線程就會從該對象的等待集中移除,重新加入線程調(diào)度。此后,它就會和其他線程一樣去爭奪該對象的鎖,一旦獲取了該對象的控制權,所有該對象的同步聲明就會重新加載。
正確使用wait()的方式如下:(等待應該總是發(fā)生在輪詢中:waits should always occur in loops)

synchronized (obj) {
       while (condition does not hold)
               obj.wait(timeout);
           ... // Perform action appropriate to condition
}

如果當前線程在等待之前或者等待期間被其他線程打斷(Thread.interrupt()),就會拋出InterruptedException異常。
該方法只應該被擁有對象monitor的線程調(diào)用。

public final native void notify();
public final native void notifyAll();

notify()喚醒等待該對象monitor的單個線程。如果多個線程都在等待該對象monitor,那么被喚醒的線程是不固定的、任意的。被喚醒的線程并不會立即執(zhí)行,而是等到當前線程放棄了該對象的鎖。被喚醒的線程會和其他線程去競爭對該對象的同步操作。也就是說,被喚醒的線程僅僅是被喚醒去參與競爭,并不是喚醒了就開始執(zhí)行。
該方法只應該被該對象的monitor的所有者線程調(diào)用。一個線程成為該對象的monitor的所有者有以下三種途徑:
①執(zhí)行該對象的同步的實例方法
②執(zhí)行一個同步塊,并且該同步了該對象
③對Class類型的對象,執(zhí)行該類的一個同步的靜態(tài)方法


為什么要使用多線程

①充分利用計算機CPU資源
假如CPU為4核,使用一個線程,那這個線程只會在一個CPU核心工作,其他三個CPU核心就浪費了。
②避免阻塞
在執(zhí)行一件耗時的任務(TASK)的時候(比如下載文件),假如只有一個線程,就只能等任務執(zhí)行完成才能執(zhí)行另外的任務。如果多線程,只需要讓一個線程去執(zhí)行下載任務,另一個線程繼續(xù)處理你其他任務,比如繼續(xù)刷網(wǎng)頁。
③并行處理任務
比如有很多任務需要執(zhí)行(比如10個人同時要下載文件),如果只有一個線程,只能逐一去下載,排在第一個的人很高興,但是后面的人需要等到前面的人下載完才能輪到自己,會很不樂意。使用多線程(假如有10個線程),每個線程對應一個下載任務,CPU就會同時(只是看上去同時)執(zhí)行這些線程,這樣每個人都可以以基本相同的速度下載完文件。
一個實際的應用就是B/S架構的應用,并行處理大量請求。
④就想到這么多。。。

內(nèi)容參考

Java多線程學習(二)---線程創(chuàng)建方式
jdk1.8
《深入理解Java虛擬機》
《Java并發(fā)編程的藝術》

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379