Java并發(fā)編程(一)線程定義、狀態(tài)和屬性

一 浦徊、線程和進程

1. 什么是線程和進程的區(qū)別:
線程是指程序在執(zhí)行過程中绍赛,能夠執(zhí)行程序代碼的一個執(zhí)行單元。在java語言中辑畦,線程有四種狀態(tài):運行 、就緒腿倚、掛起和結束纯出。
進程是指一段正在執(zhí)行的程序。而線程有事也被成為輕量級的進程敷燎,他得程序執(zhí)行的最小單元暂筝,一個進程可以擁有多個線程,各個線程之間共享程序的內功空間(代碼段硬贯、數(shù)據(jù)段和堆空間)及一些進程級的資源(例如打開的文件)焕襟,但是各個線程都擁有自己的棧空間饭豹。
2. 為何要使用多進程
在操作系統(tǒng)級別上來看主要有以下幾個方面:

  • 使用多線程可以減少程序的響應時間鸵赖,如果某個操作和耗時,或者陷入長時間的等待拄衰,此時程序講不會響應鼠標和鍵盤等的操作它褪,使用多線程后可以把這個耗時的線程分配到一個單獨的線程去執(zhí)行,從而使程序具備了更好的交互性翘悉。
  • 與進程相比茫打,線程創(chuàng)建和切換開銷更小,同時多線程在數(shù)據(jù)共享方面效率非常高妖混。
  • 多CPU或者多核計算機本身就具備執(zhí)行多線程的能力老赤,如果使用單個進程,將無法重復利用計算機資源制市,造成資源的巨大浪費抬旺。在多CPU計算機使用多線程能提高CPU的利用率。
  • 使用多線程能簡化程序的結構祥楣,使程序便于理解和維護

二嚷狞、創(chuàng)建線程
多線程的實現(xiàn)一般有以下三種方法其中前兩種為最常用的方法:
1. 繼承Thread類块促,重寫run()方法
Thread本質上也是實現(xiàn)了Runnable接口的一個實例。需要注意的是調用start()方法后并不是是立即的執(zhí)行多線程的代碼床未,而是使該線程變?yōu)榭蛇\行態(tài)竭翠,什么時候運行多線程代碼是由操作系統(tǒng)決定的。
以下是主要步驟:
(1)定義Thread類的子類薇搁,并重寫該類的run方法斋扰,該run方法的方法體就代表了線程要完成的任務。因此把run()方法稱為執(zhí)行體啃洋。
(2)創(chuàng)建Thread子類的實例传货,即創(chuàng)建了線程對象。
(3)調用線程對象的start()方法來啟動該線程宏娄。

public class TestThread extends Thread{ 
    public void run() {
            System.out.println("Hello World");
        }  
    public static void main(String[] args) {
        Thread mThread = new TestThread();
        mThread.start(); 
    } 
}

2. 實現(xiàn)Runnable接口问裕,并實現(xiàn)該接口的run()方法
以下是主要步驟:
(1)自定義類并實現(xiàn)Runnable接口,實現(xiàn)run()方法孵坚。
(2)創(chuàng)建Thread子類的實例粮宛,用實現(xiàn)Runnable接口的對象作為參數(shù)實例化該Thread對象。
(3)調用Thread的start()方法來啟動該線程卖宠。

public class TestRunnable implements Runnable {
    public void run() { 
            System.out.println("Hello World");
        } 
}

public class TestRunnable {
    public static void main(String[] args) {
        TestRunnable mTestRunnable = new TestRunnable();      
        Thread mThread = new Thread(mTestRunnable);
        mThread.start(); 
    } 
}

3. 實現(xiàn)Callable接口巍杈,重寫call()方法
Callable接口實際是屬于Executor框架中的功能類,Callable接口與Runnable接口的功能類似扛伍,但提供了比Runnable更強大的功能筷畦,主要表現(xiàn)為以下的3點:
(1)Callable可以在任務接受后提供一個返回值,Runnable無法提供這個功能刺洒。
(2)Callable中的call()方法可以拋出異常鳖宾,而Runnable的run()方法不能拋出異常。
(3)運行Callable可以拿到一個Future對象逆航,F(xiàn)uture對象表示伊布計算的結果攘滩,他提供了檢查計算是否完成的方法。由于線程屬于異步計算模型纸泡,因此無法從別的線程中得到函數(shù)的返回值漂问,在這種情況下就可以使用Future來監(jiān)視目標線程調用call()方法的情況,但調用Future的get()方法以獲取結果時女揭,當前線程就會阻塞蚤假,直到call()方法的返回結果。

public class TestCallable {  
    //創(chuàng)建線程類
    public static class MyTestCallable  implements Callable {  
        public String call() throws Exception {  
             retun "Hello World";
            }  
        }  
public static void main(String[] args) {  
        MyTestCallable mMyTestCallable= new MyTestCallable();  
        ExecutorService mExecutorService = Executors.newSingleThreadPool();  
        Future mfuture = mExecutorService.submit(mMyTestCallable);  
        try { 
        //等待線程結束吧兔,并返回結果
            System.out.println(mfuture.get());  
        } catch (Exception e) {  
           e.printStackTrace();
        } 
    }  
} 

上述程序的輸出結果為:Hello World

在這三種方式中磷仰,一般推薦實現(xiàn)Runnable接口的方式,其原因是:首先境蔼,Thread類定義了多種方法可以被派生類使用重寫灶平,但是只有run()方法是必須被重寫的伺通,實現(xiàn)這個線程的主要功能,這也是實現(xiàn)Runnable接口需要的方法逢享。其次罐监,一個類應該在他們需要加強或者修改時才會被繼承。因此如果沒有必要重寫Thread類的其他方法瞒爬,那么在這種情況下最好是用實現(xiàn)Runnable接口的方式弓柱。

三、中斷線程
當線程的run()方法執(zhí)行方法體中的最后一條語句后侧但,并經(jīng)由執(zhí)行return語句返回時矢空,或者出現(xiàn)在方法中沒有捕獲的異常時線程將終止。在java早期版本中有一個stop方法禀横,其他線程可以調用它終止線程屁药,但是這個方法現(xiàn)在已經(jīng)被棄用了。
interrupt方法可以用來請求終止線程柏锄,當一個線程調用interrupt方法時酿箭,線程的中斷狀態(tài)將被置位。這是沒有個線程都具有的boolean標志绢彤,每個線程都應該不時的檢查這個標志,來判斷線程是否被中斷蜓耻。
要想弄清線程是否被置位茫舶,可以調用Thread.currentThread().isInterrupted():

while(!Thread.currentThread().isInterrupted()){
do something
}

但是如果一個線程被阻塞,就無法檢測中斷狀態(tài)刹淌。這是產(chǎn)生InterruptedException的地方饶氏。當一個被阻塞的線程(調用sleep或者wait)上調用interrupt方法。阻塞調用將會被InterruptedException中斷有勾。
如果每次迭代之后都調用sleep方法(或者其他可中斷的方法)疹启,isInterrupted檢測就沒必要也沒用處了,如果在中斷狀態(tài)被置位時調用sleep方法蔼卡,它不會休眠反而會清除這一狀態(tài)并拋出InterruptedException喊崖。所以如果在循環(huán)中調用sleep,不要去檢測中斷狀態(tài),只需捕獲InterruptedException雇逞。
在很多發(fā)布的代碼中會發(fā)現(xiàn)InterruptedException被抑制在很低的層次上:

void myTask(){
...
try{
sleep(50)
}catch(InterruptedException e){
...
}
}

不要這樣做荤懂,如果不認為catch中做一處理有什么好處的話,有兩種合理的選擇:

  • 在catch中調用Thread.currentThread().interrup()來設置中斷狀態(tài)塘砸。調用者可以對其進行檢測
  • 更好的選擇用throw InterruptedException標記你的方法节仿,不采用try語句塊來捕獲已成。這樣調用者可以捕獲這個異常:
void myTask()throw InterruptedException{
sleep(50)
}

四掉蔬、線程的狀態(tài)

(1). 新建狀態(tài)(New):新創(chuàng)建了一個線程對象廊宪。
(2). 就緒狀態(tài)(Runnable):線程對象創(chuàng)建后矾瘾,其他線程調用了該對象的start()方法。該狀態(tài)的線程位于可運行線程池中箭启,變得可運行壕翩,等待獲取CPU的使用權。
(3). 運行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU册烈,執(zhí)行程序代碼戈泼。
(4). 阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因為某種原因放棄CPU使用權,暫時停止運行赏僧。直到線程進入就緒狀態(tài)大猛,才有機會轉到運行狀態(tài)。阻塞的情況分三種:

  • 等待阻塞:運行的線程執(zhí)行wait()方法淀零,JVM會把該線程放入等待池中挽绩。
  • 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用驾中,則JVM會把該線程放入鎖池中唉堪。
  • 其他阻塞:運行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請求時肩民,JVM會把該線程置為阻塞狀態(tài)唠亚。當sleep()狀態(tài)超時、join()等待線程終止或者超時持痰、或者I/O處理完畢時灶搜,線程重新轉入就緒狀態(tài)。
    (5). 死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法工窍,該線程結束生命周期割卖。
這里寫圖片描述

五、線程的優(yōu)先級和守護線程

1. 線程優(yōu)先級
在java中患雏,每一個線程有一個優(yōu)先級鹏溯,默認情況下,一個線程繼承它父類的優(yōu)先級淹仑”欤可以用setPriority方法提高或降低任何一個線程優(yōu)先級≡冉瑁可以將優(yōu)先級設置在MIN_PRIORITY(在Thread類定義為1)與MAX_PRIORITY(在Thread類定義為10)之間的任何值取试。線程的默認優(yōu)先級為NORM_PRIORITY(在Thread類定義為5)。
盡量不要依賴優(yōu)先級怀吻,如果確實要用瞬浓,應該避免初學者常犯的一個錯誤。如果有幾個高優(yōu)先級的線程沒有進入非活動狀態(tài)蓬坡,低優(yōu)先級線程可能永遠也不能執(zhí)行猿棉。每當調度器決定運行一個新線程時胸梆,首先會在具有搞優(yōu)先級的線程中進行選擇畸冲,盡管這樣會使低優(yōu)先級的線程完全餓死。

2. 守護線程

調用setDaemon(true);將線程轉換為守護線程。守護線程唯一的用途就是為其他線程提供服務攒发。計時線程就是一個例子宛官,他定時發(fā)送信號給其他線程或者清空過時的告訴緩存項的線程帚豪。當只剩下守護線程時空免,虛擬機就退出了,由于如果只剩下守護線程慰安,就沒必要繼續(xù)運行程序了腋寨。
另外JVM的垃圾回收、內存管理等線程都是守護線程化焕。還有就是在做數(shù)據(jù)庫應用時候萄窜,使用的數(shù)據(jù)庫連接池,連接池本身也包含著很多后臺線程撒桨,監(jiān)控連接個數(shù)查刻、超時時間、狀態(tài)等等凤类。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末穗泵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谜疤,更是在濱河造成了極大的恐慌佃延,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茎截,死亡現(xiàn)場離奇詭異苇侵,居然都是意外死亡赶盔,警方通過查閱死者的電腦和手機企锌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來于未,“玉大人撕攒,你說我怎么就攤上這事『嫫郑” “怎么了抖坪?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闷叉。 經(jīng)常有香客問我擦俐,道長,這世上最難降的妖魔是什么握侧? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任蚯瞧,我火速辦了婚禮嘿期,結果婚禮上,老公的妹妹穿的比我還像新娘埋合。我一直安慰自己备徐,他們只是感情好,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布甚颂。 她就那樣靜靜地躺著蜜猾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪振诬。 梳的紋絲不亂的頭發(fā)上蹭睡,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機與錄音贷揽,去河邊找鬼棠笑。 笑死,一個胖子當著我的面吹牛禽绪,可吹牛的內容都是我干的蓖救。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼印屁,長吁一口氣:“原來是場噩夢啊……” “哼循捺!你這毒婦竟也來了?” 一聲冷哼從身側響起雄人,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤从橘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后础钠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恰力,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年旗吁,在試婚紗的時候發(fā)現(xiàn)自己被綠了踩萎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡很钓,死狀恐怖香府,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情码倦,我是刑警寧澤企孩,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站袁稽,受9級特大地震影響勿璃,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一补疑、第九天 我趴在偏房一處隱蔽的房頂上張望闻葵。 院中可真熱鬧,春花似錦癣丧、人聲如沸槽畔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厢钧。三九已至,卻和暖如春嬉橙,著一層夾襖步出監(jiān)牢的瞬間早直,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工市框, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霞扬,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓枫振,卻偏偏與公主長得像喻圃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子粪滤,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內容

  • 本文主要講了java中多線程的使用方法斧拍、線程同步、線程數(shù)據(jù)傳遞杖小、線程狀態(tài)及相應的一些線程函數(shù)用法肆汹、概述等。 首先講...
    李欣陽閱讀 2,444評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,952評論 1 18
  • 3. 創(chuàng)建新線程 創(chuàng)建一個線程有兩種方法: 從Thread類繼承并重寫run()方法予权。創(chuàng)建一個實例昂勉,調用star...
    vancent閱讀 469評論 0 0
  • 前言 多線程并發(fā)編程是Java編程中重要的一塊內容,也是面試重點覆蓋區(qū)域扫腺,所以學好多線程并發(fā)編程對我們來說極其重要...
    嘟爺MD閱讀 7,308評論 21 272
  • 線程概述 線程與進程 進程 ?每個運行中的任務(通常是程序)就是一個進程岗照。當一個程序進入內存運行時,即變成了一個進...
    閩越布衣閱讀 1,008評論 1 7