JAVA多線程(一)

線程的介紹

概念:線程是程序的執(zhí)行路徑,每一個線程都有自己的局部變量久妆,程序計數(shù)器晌杰,以及各自的生命周期。

快速啟動一個線程

認識Thread Class類

public static void main(String [] args){
  new Thread(){
    @Override
    public void run(){
        enjoyMusic();    
}
}.start();
 enjoyNews();

public static void enjoyMusic(){
    for(;;){
      System.out.println("我在播放音樂");
  }
}

public static void enjoyNews(){
    for(;;){
        System.out.println("我在看新聞呢!");
}
}
執(zhí)行結果
我在播放音樂和我在看新聞呢筷弦! 交替輸出
}

使用Jconsole觀察線程

可以使用JDK的Bin目錄下的Jconsole.exe 觀察當前線程的信息

線程的生命周期

問題:線程Thread執(zhí)行了start方法就表示該線程已近執(zhí)行了忙肋演?
下面看線程生命周期圖


線程的生命周期

由以上圖可以知道,線程的生命周期大致可以分為以下五個階段:
NEW
RUNNABLE
RUNNING
BLOCKED
TERMINATED

1.NEW狀態(tài)
當我們new出一個Thread類的時候,此時它并不在執(zhí)行狀態(tài)惋啃,并且它并沒有調用Start方法哼鬓,那么此時線程的狀態(tài)為New监右。
NEW狀態(tài)通過Start 方法進入RUNNABLE 狀態(tài)
2.RUNNABLE狀態(tài)
一個線程new出來之后边灭,通過調用start方法進入RUNNABLE 狀態(tài),此時線程正在會立即執(zhí)行忙健盒?答案是否定的绒瘦,線程和進程一樣 都要聽命于CPU調度(其實是在等待獲取CPU時間片),此時它只具備可執(zhí)行資格扣癣,但是并沒有真正的執(zhí)行惰帽,
并且線程存在RUNNING 狀態(tài) 不會一下就進入阻塞狀態(tài)(BLOCKED)狀態(tài)和消亡狀態(tài)(TERMINATED),即使是在線程運行期間執(zhí)行父虑,wait,sleep或者其他阻塞該線程的操作该酗,也必須獲得CPU調度的執(zhí)行權 才可以。RUNNABLE 的線程只能進入RUNNING狀態(tài)或者意外終止士嚎。
3.RUNNING 狀態(tài)
一旦CPU輪詢或者其他方式從任務可執(zhí)行隊列中選中了線程呜魄,才能真正的執(zhí)行自己的代碼,正在RUNNING 的線程其實也是RUNNABLE 的莱衩,則反過來不成立爵嗅,在該狀態(tài)中,線程可以發(fā)生如下的狀態(tài)改變笨蚁。

  • 直接進入消亡狀態(tài)(TERMINATED),比如執(zhí)行了JDK已近不推薦的Stop方法睹晒。
  • 進入阻塞狀態(tài)(BLOCKED) ,如調用了Sleep()方法括细,或者wait方法伪很。
  • 進行了阻塞的IO操作,從而網(wǎng)絡數(shù)據(jù)的讀寫進入了BLOCKED 狀態(tài) 奋单。
  • 獲取某個資源是掰,從而加入到改鎖的阻塞隊列 ,從而進入了 BLOCKED狀態(tài)辱匿。
  • 由于CPU調度器輪詢使該線程放棄執(zhí)行键痛,進入BLOCKED狀態(tài)。
  • 線程主動調用yield,放棄CPU執(zhí)行權匾七,進入BLOCKED狀態(tài)絮短。
    4.BLOCKED狀態(tài)
    BLOCKED狀態(tài)跟RUNNING狀態(tài)類似,可以發(fā)生如下的狀態(tài)改變
  • 直接進入消亡狀態(tài)(TERMINATED),比如執(zhí)行了JDK已近不推薦的Stop方法昨忆。
  • 線程阻塞的操作結束丁频,比如讀取到了想要讀取的數(shù)據(jù),進入RUNNABLED狀態(tài),
  • 線程完成了休眠的狀態(tài)席里,進入到了RUNNABLED狀態(tài)
  • Wait中的線程被其他線程喚醒(notify,notifyall)叔磷,進入到RUNNABLED狀態(tài)
  • 線程獲取到了某個鎖資源,進入到了RUNNABLED狀態(tài)
  • 線程在阻塞過程中奖磁,被打斷(調用Interrupt)改基,從而進入到了RUNNABLED狀態(tài),
    5.TERMINATED 狀態(tài)
    TERMINATED 是一個最終 的狀態(tài)咖为,該線程的狀態(tài)不會有TERMINATED 轉換到其他的狀態(tài)秕狰,進入到TERMINATED 狀態(tài),意味著該線程的聲明周期結束了躁染,以下操作鸣哀,會讓線程進入TERMINATED 狀態(tài)
  • 線程正常結束,進入TERMINATED狀態(tài)
  • 線程運行出錯吞彤,意外終止
  • JVM Crash 所有線程都結束

線程Start方法剖析

一個線程Thread new 出來之后我衬,重寫run()方法,并且 調用start方法饰恕,進入到了可執(zhí)行狀態(tài)(RUNNABLED)挠羔。那么run方法和start方法究竟是什么關系呢?看源碼

private volatile int threadStatus = 0;
public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

以上代碼最關鍵的部分是調用了start0()這個方法懂盐,重寫Run方法何時被調用了呢褥赊?在開始一個線程的時候JVM會調用該線程的Run方法,run方法是被JNI方法start0()調用的莉恼,查看源碼我們發(fā)現(xiàn)以下問題:
1.當Thread被構造出new狀態(tài)后拌喉,他的threadStatus 默認為0
2.不能兩次啟動,兩次調用start方法,否則機會拋出IllegalThreadStateException 異常
3.線程啟動會被加入ThreadGroup中俐银,
4.一個線程的生命周期結束尿背,也就是到了TERMINATED 狀態(tài),再次start方法捶惜,是不被允許的田藐,也就是說TERMINATED 狀態(tài)沒有辦法回到,RUNNABLED/RUNNING狀態(tài)的

Thread t = new Thread(new Runnable() {
    @Override
     public void run() {
      sleep(10);   
    }
   });
 t.start();
 t.start();

上述代碼會拋出 IllegalThreadStateException 異常吱七,我們可以改動一下模擬線程的聲明周期結束汽久。

    Thread t = new Thread(new Runnable() {
    @Override
     public void run() {
      sleep(1);   
    }
   });
    t.start();
     TimeUnit.SECONDS.sleep(2);//休眠是確保thread結束聲明周期
    t.start();//重新激活線程

以上兩個例子都會拋出異常 ,但是意義是不一樣的踊餐,第一個線程不能被啟動兩次景醇,第二個啟動是不被允許的。

模板模式在Thread中的應用

通過上述代碼吝岭,我們不難看出三痰,線程執(zhí)行的業(yè)務邏輯在Run方法內部吧寺,我們用重寫run方法 用start方法啟動線程,其實Thread中的run方法和start是一個比較典型的模板設計模式散劫,父類定義算法結構代碼稚机,子類實現(xiàn)業(yè)務邏輯。

下面舉個列子
銀行有四臺取號機获搏,每臺取號機都有排隊等候赖条,當你進入業(yè)務大廳的時候需要取號,領取一張紙質號碼颜凯,假設有四臺出號機器谋币, 每天最對受理30筆業(yè)務仗扬,ticketShow 代表出號機器

public class ticketShow extends Thread{
     public final String name;
     public static final int MAX = 50;
    private int index = 1;
    ticketShow (String name){
        this.name = name;
    }
 @Overried
public void run(){
    while(index <= MAX){
      System.out.println("當前機器:"+name+",出的號碼是:"+(index++));  
}   
}
}

接下來編寫一個main方法
public static void main(String [] args){
      ticketShow  ts = new ticketShow ("一號機器");
      ts.start();
      ticketShow  ts = new ticketShow ("二號機器");
      ts.start();
      ticketShow  ts = new ticketShow ("三號機器");
      ts.start();
      ticketShow  ts = new ticketShow ("四號機器");
      ts.start();
}

然而每臺機器都是從1到50症概,四個線程并沒有交互,獲取唯一一個遞增的號碼早芭,那么我們改進一下

public class ticketShow extends Thread{
     public final String name;
     public static final int MAX = 50;
    private static int index = 1;
    ticketShow (String name){
        this.name = name;
    }
 @Overried
public void run(){
    while(index <= MAX){
      System.out.println("當前機器:"+name+",出的號碼是:"+(index++));  
}   
}
}

再次運行彼城,發(fā)現(xiàn)四個出號機器,交替輸出不同的號碼退个,
通過對Index進行static 修飾募壕,做到了多線程下共享資源的唯一性,看起來似乎滿足了我們的需求语盈,但是還是有些問題 比如我們的共享資源很多呢舱馅,要經(jīng)過一些復雜的計算呢?等等等刀荒。代嗤。。

策略模式在Runnable在thread中的應用

很多文章中都會說缠借,創(chuàng)建線程有兩種方式干毅,第一個是構造Thread,第二個是構造Runnable 其實這種說法是錯誤的泼返,最起碼是不嚴謹?shù)南醴辏瑴蚀_來說,創(chuàng)建線程只有一種方式绅喉,那就是構造Thread類渠鸽,而實現(xiàn)線程的執(zhí)行單元有兩種方式,第一個是重寫Thread中的run方法柴罐,第二個是重寫runnable中的run方法徽缚,當然他們還是有些不同的,那就是Thread類中的run方法是不能共享的丽蝎,也就是說線程A不能把線程B中的run方法當做自己的執(zhí)行單元猎拨,然而runnable接口就很容易實現(xiàn)這一點膀藐。使用不同的runable構造不同的thread實例。
那我們對銀行取票機代碼進行 重構一下

public class ticketShow implements runnable{
     public final String name;
     public static final int MAX = 50;
    private int index = 1; //不做static 修飾
    ticketShow (String name){
        this.name = name;
    }
 @Overried
public void run(){
    while(index <= MAX){
      System.out.println("當前機器:"+name+",出的號碼是:"+(index++));  
    sleep(100);
}   
}
}

添加main方法
public static void main(String [] args){
    final  ticketShow  ts = new ticketShow ();
    Thread t1 = new Thread(ts,"一號機器");
    t1.start();
   Thread t2 = new Thread(ts,"二號機器");
    t2.start();
   Thread t3 = new Thread(ts,"三號機器");
    t3.start();
   Thread t4 = new Thread(ts,"四號機器");
    t4.start();
}

可以看到上述代碼的輸入和繼承Thread類實現(xiàn)并且是同樣的效果红省,四個較好機器線程都實現(xiàn)了同一個接口Runnable额各,這樣她們的資源都是共享的,不會出現(xiàn)每一個叫號機器從一到五十的情況吧恃。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末虾啦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痕寓,更是在濱河造成了極大的恐慌傲醉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呻率,死亡現(xiàn)場離奇詭異硬毕,居然都是意外死亡,警方通過查閱死者的電腦和手機礼仗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門吐咳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人元践,你說我怎么就攤上這事韭脊。” “怎么了单旁?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵沪羔,是天一觀的道長。 經(jīng)常有香客問我象浑,道長蔫饰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任融柬,我火速辦了婚禮死嗦,結果婚禮上,老公的妹妹穿的比我還像新娘粒氧。我一直安慰自己越除,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布外盯。 她就那樣靜靜地躺著摘盆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饱苟。 梳的紋絲不亂的頭發(fā)上孩擂,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音箱熬,去河邊找鬼类垦。 笑死狈邑,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蚤认。 我是一名探鬼主播米苹,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼砰琢!你這毒婦竟也來了蘸嘶?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤陪汽,失蹤者是張志新(化名)和其女友劉穎训唱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體挚冤,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡况增,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了你辣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巡通。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡尘执,死狀恐怖舍哄,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情誊锭,我是刑警寧澤表悬,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站丧靡,受9級特大地震影響蟆沫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜温治,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一饭庞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧熬荆,春花似錦舟山、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至突琳,卻和暖如春若债,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拆融。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工蠢琳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啊终,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓傲须,卻偏偏與公主長得像孕索,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子躏碳,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容