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

1、線程創(chuàng)建

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

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

  • 實現(xiàn)Runnable接口
    ①定義runnable接口的實現(xiàn)類啦鸣,并重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執(zhí)行體来氧。
    ②創(chuàng)建 Runnable實現(xiàn)類的實例诫给,并依此實例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象啦扬。
    ③調用線程對象的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)建并啟動新線程赐写。
    ④調用FutureTask對象的get()方法來獲得子線程執(zhí)行結束后的返回值

示例代碼

public class ThreadLearning {
    
//------------------------繼承Thread類-----------------------
    //匿名內部類的形式
    @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接口-----------------------
    //匿名內部類的形式
    @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()方法。也就是說還未調用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)的原因是調用了下面三種方法之一:
     * ①沒有設置Timeout參數(shù)的Object.wait()方法
     * ②沒有設置Timeout參數(shù)的Thread.join()方法
     * ③LockSupport.park()方法
     * 
     * 
     */
    WAITING,
    /**
     * 
     * 限期等待狀態(tài)
     * 
     * 線程進入限期等待狀態(tài)的原因是調用了下面五種方法之一:
     * 
     * ①設置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();

該方法向線程調度器提示讯屈,當前線程(調用該方法的線程對象代表的線程)愿意讓出處理器的使用權,但是線程調度器可能會忽視該提示县习。
很少有適合使用該方法的地方涮母。它可能在調試或者測試的時候,幫助重現(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;

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

public synchronized void start()

該方法會讓當前線程(調用該方法的線程對象代表的線程)開始執(zhí)行逸雹。JVM虛擬機會調用該線程的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

該方法會讓當前線程(調用該方法的線程峡眶,注意剧防,這里并不是thread.join()中thread所表示的線程,而是方法thread.join()所在的線程)等待指定時間(如果為0辫樱,表示一直等到thread執(zhí)行完)峭拘,讓join()方法的所屬線程thread執(zhí)行。
join()方法內部是調用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

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

上述事件之一發(fā)生后,該線程就會從該對象的等待集中移除脖旱,重新加入線程調度堪遂。此后介蛉,它就會和其他線程一樣去爭奪該對象的鎖,一旦獲取了該對象的控制權溶褪,所有該對象的同步聲明就會重新加載币旧。
正確使用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的線程調用佳恬。

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

notify()喚醒等待該對象monitor的單個線程捏境。如果多個線程都在等待該對象monitor于游,那么被喚醒的線程是不固定的、任意的垫言。被喚醒的線程并不會立即執(zhí)行贰剥,而是等到當前線程放棄了該對象的鎖。被喚醒的線程會和其他線程去競爭對該對象的同步操作筷频。也就是說蚌成,被喚醒的線程僅僅是被喚醒去參與競爭,并不是喚醒了就開始執(zhí)行凛捏。
該方法只應該被該對象的monitor的所有者線程調用担忧。一個線程成為該對象的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架構的應用梨水,并行處理大量請求拭荤。
④就想到這么多。疫诽。舅世。

內容參考

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奇徒,隨后出現(xiàn)的幾起案子雏亚,更是在濱河造成了極大的恐慌,老刑警劉巖摩钙,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罢低,死亡現(xiàn)場離奇詭異,居然都是意外死亡胖笛,警方通過查閱死者的電腦和手機网持,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來长踊,“玉大人功舀,你說我怎么就攤上這事∩肀祝” “怎么了辟汰?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長佑刷。 經(jīng)常有香客問我莉擒,道長,這世上最難降的妖魔是什么瘫絮? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任涨冀,我火速辦了婚禮,結果婚禮上麦萤,老公的妹妹穿的比我還像新娘鹿鳖。我一直安慰自己,他們只是感情好壮莹,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布翅帜。 她就那樣靜靜地躺著,像睡著了一般命满。 火紅的嫁衣襯著肌膚如雪涝滴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音歼疮,去河邊找鬼杂抽。 笑死,一個胖子當著我的面吹牛韩脏,可吹牛的內容都是我干的缩麸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赡矢,長吁一口氣:“原來是場噩夢啊……” “哼杭朱!你這毒婦竟也來了?” 一聲冷哼從身側響起吹散,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤弧械,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后送浊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梦谜,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡丘跌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年袭景,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闭树。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸棒,死狀恐怖,靈堂內的尸體忽然破棺而出报辱,到底是詐尸還是另有隱情与殃,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布碍现,位于F島的核電站幅疼,受9級特大地震影響,放射性物質發(fā)生泄漏昼接。R本人自食惡果不足惜爽篷,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望慢睡。 院中可真熱鬧逐工,春花似錦、人聲如沸漂辐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽髓涯。三九已至袒啼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蚓再。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工灶泵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人对途。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓赦邻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親实檀。 傳聞我的和親對象是個殘疾皇子惶洲,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355