Java并發(fā)指南1:并發(fā)基礎(chǔ)與Java多線程

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫猬膨,更多精彩內(nèi)容請到我的倉庫里查看

https://github.com/h2pl/Java-Tutorial

喜歡的話麻煩點下Star哈

文章同步發(fā)于我的個人博客:

www.how2playlife.com

本文是微信公眾號【Java技術(shù)江湖】的《Java并發(fā)指南》其中一篇议慰,本文大部分內(nèi)容來源于網(wǎng)絡(luò)蛔趴,為了把本文主題講得清晰透徹,也整合了很多我認(rèn)為不錯的技術(shù)博客內(nèi)容旷赖,引用其中了一些比較好的博客文章,如有侵權(quán)萧福,請聯(lián)系作者崇摄。

該系列博文會告訴你如何全面深入地學(xué)習(xí)Java并發(fā)技術(shù)擎值,從Java多線程基礎(chǔ),再到并發(fā)編程的基礎(chǔ)知識配猫,從Java并發(fā)包的入門和實戰(zhàn)幅恋,再到JUC的源碼剖析,一步步地學(xué)習(xí)Java并發(fā)編程泵肄,并上手進(jìn)行實戰(zhàn)捆交,以便讓你更完整地了解整個Java并發(fā)編程知識體系,形成自己的知識框架腐巢。

為了更好地總結(jié)和檢驗?zāi)愕膶W(xué)習(xí)成果品追,本系列文章也會提供一些對應(yīng)的面試題以及參考答案。

如果對本系列文章有什么建議冯丙,或者是有什么疑問的話肉瓦,也可以關(guān)注公眾號【Java技術(shù)江湖】聯(lián)系作者,歡迎你參與本系列博文的創(chuàng)作和修訂胃惜。

1多線程的優(yōu)點

    • 資源利用率更好
    • 程序設(shè)計在某些情況下更簡單
    • 程序響應(yīng)更快

1.1資源利用率更好案例

方式1
從磁盤讀取一個文件需要5秒泞莉,處理一個文件需要2秒。處理兩個文件則需要14秒

1    5秒讀取文件A2    2秒處理文件A3    5秒讀取文件B4    2秒處理文件B5    ---------------------6    總共需要14秒

方式2
從磁盤中讀取文件的時候船殉,大部分的CPU非常的空閑鲫趁。它可以做一些別的事情。通過改變操作的順序利虫,就能夠更好的使用CPU資源挨厚”てВ看下面的順序:

1    5秒讀取文件A2    5秒讀取文件B + 2秒處理文件A3    2秒處理文件B4    ---------------------5    總共需要12秒

總結(jié):多線程并發(fā)效率提高2秒

1.2程序響應(yīng)更快

設(shè)想一個服務(wù)器應(yīng)用,它在某一個端口監(jiān)聽進(jìn)來的請求疫剃。當(dāng)一個請求到來時钉疫,它把請求傳遞給工作者線程(worker thread),然后立刻返回去監(jiān)聽巢价。而工作者線程則能夠處理這個請求并發(fā)送一個回復(fù)給客戶端牲阁。

 while(server is active){
        listenThread for request
       hand request to workerThread
    }

這種方式,服務(wù)端線程迅速地返回去監(jiān)聽蹄溉。因此咨油,更多的客戶端能夠發(fā)送請求給服務(wù)端您炉。這個服務(wù)也變得響應(yīng)更快柒爵。

2多線程的代價

2.1設(shè)計更復(fù)雜

多線程一般都復(fù)雜。在多線程訪問共享數(shù)據(jù)的時候赚爵,這部分代碼需要特別的注意棉胀。線程之間的交互往往非常復(fù)雜。不正確的線程同步產(chǎn)生的錯誤非常難以被發(fā)現(xiàn)冀膝,并且重現(xiàn)以修復(fù)唁奢。

2.2上下文切換的開銷

上下文切換當(dāng)CPU從執(zhí)行一個線程切換到執(zhí)行另外一個線程的時候,它需要先存儲當(dāng)前線程的本地的數(shù)據(jù)窝剖,程序指針等麻掸,然后載入另一個線程的本地數(shù)據(jù),程序指針等赐纱,最后才開始執(zhí)行脊奋。

CPU會在一個上下文中執(zhí)行一個線程,然后切換到另外一個上下文中執(zhí)行另外一個線程疙描。

上下文切換并不廉價诚隙。如果沒有必要,應(yīng)該減少上下文切換的發(fā)生起胰。

2.3增加資源消耗

每個線程需要消耗的資源:

CPU久又,內(nèi)存(維持它本地的堆棧),操作系統(tǒng)資源(管理線程)

3競態(tài)條件與臨界區(qū)

當(dāng)多個線程競爭同一資源時效五,如果對資源的訪問順序敏感地消,就稱存在競態(tài)條件。導(dǎo)致競態(tài)條件發(fā)生的代碼區(qū)稱作臨界區(qū)畏妖。

多線程同時執(zhí)行下面的代碼可能會出錯:

public class Counter {
    protected long count = 0;
 
    public void add(long value) {
        this.count = this.count + value;
    }
}

想象下線程A和B同時執(zhí)行同一個Counter對象的add()方法脉执,我們無法知道操作系統(tǒng)何時會在兩個線程之間切換。JVM并不是將這段代碼視為單條指令來執(zhí)行的瓜客,而是按照下面的順序

從內(nèi)存獲取 this.count 的值放到寄存器
將寄存器中的值增加value
將寄存器中的值寫回內(nèi)存
 
 
 
觀察線程A和B交錯執(zhí)行會發(fā)生什么
 
    this.count = 0;
   A:   讀取 this.count 到一個寄存器 (0)
   B:   讀取 this.count 到一個寄存器 (0)
   B:   將寄存器的值加2
   B:   回寫寄存器值(2)到內(nèi)存. this.count 現(xiàn)在等于 2
   A:   將寄存器的值加3

由于兩個線程是交叉執(zhí)行的适瓦,兩個線程從內(nèi)存中讀出的初始值都是0竿开。然后各自加了2和3,并分別寫回內(nèi)存玻熙。最終的值可能并不是期望的5否彩,而是最后寫回內(nèi)存的那個線程的值,上面例子中最后寫回內(nèi)存可能是線程A嗦随,也可能是線程B

4線程的運行與創(chuàng)建

Java 創(chuàng)建線程對象有兩種方法:

  • 繼承 Thread 類創(chuàng)建線程對象
  • 實現(xiàn) Runnable 接口類創(chuàng)建線程對象

注意:

在java中列荔,每次程序運行至少啟動2個線程。一個是main線程枚尼,一個是垃圾收集線程贴浙。因為每當(dāng)使用java命令執(zhí)行一個類的時候,實際上都會啟動一個jvm署恍,每一個jvm實際上就是在操作系統(tǒng)中啟動了一個進(jìn)程崎溃。

5線程的狀態(tài)和優(yōu)先級

線程優(yōu)先級1 到 10 ,其中 1 是最低優(yōu)先級盯质,10 是最高優(yōu)先級袁串。

狀態(tài)

  • new(新建)
  • runnnable(可運行)
  • blocked(阻塞)
  • waiting(等待)
  • time waiting (定時等待)
  • terminated(終止)

狀態(tài)轉(zhuǎn)換

線程狀態(tài)流程如下:

  • 線程創(chuàng)建后,進(jìn)入 new 狀態(tài)
  • 調(diào)用 start 或者 run 方法呼巷,進(jìn)入 runnable 狀態(tài)
  • JVM 按照線程優(yōu)先級及時間分片等執(zhí)行 runnable 狀態(tài)的線程囱修。開始執(zhí)行時,進(jìn)入 running 狀態(tài)
  • 如果線程執(zhí)行 sleep王悍、wait破镰、join,或者進(jìn)入 IO 阻塞等压储。進(jìn)入 wait 或者 blocked 狀態(tài)
  • 線程執(zhí)行完畢后鲜漩,線程被線程隊列移除。最后為 terminated 狀態(tài)

代碼

public class MyThreadInfo extends Thread {
 
    @Override // 可以省略
    public void run() {
        System.out.println("run");
        // System.exit(1);
    }
 
    public static void main(String[] args) {
        MyThreadInfo thread = new MyThreadInfo();
        thread.start();
 
        System.out.println("線程唯一標(biāo)識符:" + thread.getId());
        System.out.println("線程名稱:" + thread.getName());
        System.out.println("線程狀態(tài):" + thread.getState());
        System.out.println("線程優(yōu)先級:" + thread.getPriority());
    }
}
 
結(jié)果:
線程唯一標(biāo)識符:9
線程名稱:Thread-0
run
線程狀態(tài):RUNNABLE
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渠脉,一起剝皮案震驚了整個濱河市宇整,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芋膘,老刑警劉巖鳞青,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異为朋,居然都是意外死亡臂拓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進(jìn)店門习寸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胶惰,“玉大人,你說我怎么就攤上這事霞溪》踔停” “怎么了中捆?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長坊饶。 經(jīng)常有香客問我泄伪,道長,這世上最難降的妖魔是什么匿级? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任蟋滴,我火速辦了婚禮,結(jié)果婚禮上痘绎,老公的妹妹穿的比我還像新娘津函。我一直安慰自己,他們只是感情好孤页,可當(dāng)我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布尔苦。 她就那樣靜靜地躺著,像睡著了一般散庶。 火紅的嫁衣襯著肌膚如雪蕉堰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天悲龟,我揣著相機與錄音,去河邊找鬼冰寻。 笑死须教,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斩芭。 我是一名探鬼主播轻腺,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼划乖!你這毒婦竟也來了贬养?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤琴庵,失蹤者是張志新(化名)和其女友劉穎误算,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迷殿,經(jīng)...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡儿礼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了庆寺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚊夫。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖懦尝,靈堂內(nèi)的尸體忽然破棺而出知纷,到底是詐尸還是另有隱情壤圃,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布琅轧,位于F島的核電站埃唯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鹰晨。R本人自食惡果不足惜墨叛,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望模蜡。 院中可真熱鬧漠趁,春花似錦、人聲如沸忍疾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卤妒。三九已至甥绿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間则披,已是汗流浹背共缕。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留士复,地道東北人图谷。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像阱洪,于是被迫代替她去往敵國和親便贵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,666評論 2 350

推薦閱讀更多精彩內(nèi)容