什么是多線程
多線程的意思是在一個(gè)程序里有多個(gè)線程執(zhí)行预烙。一個(gè)線程就像一個(gè)獨(dú)立的CPU在執(zhí)行你的程序酬屉。所以负懦,多線程程序就像是有多個(gè)CPU在同一時(shí)間執(zhí)行不同部分代碼的程序。
但是一個(gè)線程不是一個(gè)CPU缩滨。通常一個(gè)單獨(dú)的CPU會在多線程之間共享它的執(zhí)行時(shí)間势就,在線程執(zhí)行了一定時(shí)間后進(jìn)行切換。多線程也可能被不同的CPU執(zhí)行脉漏。
為什么要用多線程
使用多線程的原因有很多苞冯。最通常的原因有:
- 更好利用單個(gè)CPU
- 更好利用多個(gè)CPU或CPU核
- 在響應(yīng)方面,有更好的用戶體驗(yàn)
- 在公平方面侧巨,有更好的用戶體驗(yàn)
更好利用單個(gè)CPU
一個(gè)最普遍的原因是為了能夠更好的利用計(jì)算機(jī)資源舅锄。例如,如果一個(gè)線程正在等待網(wǎng)絡(luò)上的一個(gè)請求司忱,那么另一個(gè)線程能夠在此時(shí)利用CPU去做其它的事情皇忿。另外,如果計(jì)算機(jī)有多個(gè)CPU坦仍,或者CPU上有多個(gè)執(zhí)行核心鳍烁,那么多線程能夠幫忙你的程序利用這些額外的CPU核。
更好利用多個(gè)CPU或CPU核
如果計(jì)算機(jī)有多個(gè)CPU繁扎,或者CPU上有多個(gè)執(zhí)行核心幔荒,那么你需要在你的程序中使用多線程去利用所有的CPU或CPU核心糊闽。一個(gè)的線程最多利用一個(gè)CPU,就像上面提到那樣爹梁,有時(shí)還不能完成利用一個(gè)CPU右犹。
在響應(yīng)方面,有更好的用戶體驗(yàn)
另一個(gè)使用多線程的原因是為了提供更好的用戶體驗(yàn)姚垃。例如傀履,如果你在一個(gè)界面上點(diǎn)擊了一個(gè)按鈕,發(fā)起了一個(gè)網(wǎng)絡(luò)上的請求莉炉,那么由哪個(gè)線程來執(zhí)行這個(gè)請求很重要。如果你用更新界面的那個(gè)線程碴犬,那些在等待響應(yīng)期間絮宁,用戶可能會感覺到界面卡了。相反服协,這個(gè)請求可以被后臺一個(gè)線程執(zhí)行绍昂,那么更新界面的線程就能夠繼續(xù)響應(yīng)用戶的其他請求。
在公平方面偿荷,有更好的用戶體驗(yàn)
第4原因是在用戶之前更公平的共享資源窘游。想像有一臺服務(wù)器,它接受從客戶端來的請求跳纳,并且只有一個(gè)線程去執(zhí)行這些請求忍饰。如果一個(gè)客戶端發(fā)送了一個(gè)需要處理很長時(shí)間的請求,那么所以其他客戶端的請求將不得不等待那個(gè)請求完成寺庄。如果讓每個(gè)客戶端的請求在它們自己的線程中執(zhí)行艾蓝,那么就沒有一個(gè)任務(wù)能夠完全獨(dú)占CPU。
多線程與多任務(wù)對比
在過去斗塘,計(jì)算機(jī)只有一個(gè)CPU并且一次只能執(zhí)行一個(gè)程序赢织。大部分小型機(jī)不夠強(qiáng)大去同時(shí)執(zhí)行多個(gè)程序,所以并沒有嘗試馍盟。公平的說于置,許多大型機(jī)系統(tǒng)在同時(shí)執(zhí)行多個(gè)程序的方面比個(gè)人電腦要多許多年。
多任務(wù)
之后多任務(wù)的出現(xiàn)贞岭,說明計(jì)算機(jī)能夠同時(shí)執(zhí)行多個(gè)程序(也就是任務(wù)或進(jìn)程)八毯。但它并不是真正的“同時(shí)”。CPU被多個(gè)程序共享瞄桨。操作系統(tǒng)會控制每個(gè)任務(wù)在執(zhí)行一小段時(shí)間后進(jìn)行切換宪彩。
多任務(wù)給軟件開發(fā)者帶來了新挑戰(zhàn)。程序員不再假定能夠獲取所有的CPU時(shí)間讲婚,或所有內(nèi)存或所有其他資源尿孔。一個(gè)“好市民”的程序,在不需要使用時(shí)釋放所有資源,以便其它程序能夠使用活合。
多線程
后來多線程出現(xiàn)雏婶,意味著在一個(gè)程序里能夠使用多個(gè)線程執(zhí)行。一個(gè)線程執(zhí)行可以想像成一個(gè)CPU在執(zhí)行程序白指。當(dāng)你使用多線程時(shí)留晚,就像有多個(gè)CPU執(zhí)行同個(gè)程序。
多線程是困難的
多線程能夠非常有效提高某此程序的性能告嘲。然而错维,多線程比多任務(wù)更有挑戰(zhàn)性。多個(gè)線程在同個(gè)程序中執(zhí)行橄唬,所以會同時(shí)讀取與寫入內(nèi)存赋焕。這會導(dǎo)致一些單線程程序里沒有的錯誤。這些錯誤在單CPU的機(jī)器可能不會出現(xiàn)仰楚,因?yàn)閮蓚€(gè)線程不可能同時(shí)執(zhí)行隆判。然而,現(xiàn)代計(jì)算機(jī)有多個(gè)CPU核心僧界,甚至也有多個(gè)CPU侨嘀。這意味著不同線程能夠同時(shí)地被不同的CPU執(zhí)行。
如果一個(gè)線程讀取的內(nèi)存位置捂襟,有另一個(gè)線程在寫入咬腕,那么第一個(gè)線程會讀取到什么值?舊值葬荷?還是第二個(gè)線程寫入的值郎汪?或者是兩者間的混合值?又如果闯狱,兩個(gè)線程同時(shí)寫入相同的內(nèi)存位置煞赢,最后會是什么值?第一個(gè)線程寫入的值哄孤?第二個(gè)線程寫入的值照筑?又或者各寫一半的值?
沒有適當(dāng)?shù)念A(yù)防措施瘦陈,以上的結(jié)果都有可能凝危。上述行為并不能被預(yù)知。結(jié)果每次都會不同晨逝。所以開發(fā)者懂得采取正確的預(yù)防措施是很重要的:意思是學(xué)習(xí)如何控制線程訪問共享資源蛾默,如內(nèi)存、文件和數(shù)據(jù)庫等捉貌。
并發(fā)模型
Java并發(fā)模型最初假設(shè)多個(gè)線程在相同的程序中執(zhí)行并且共享對象支鸡。這種模型被稱為“共享狀態(tài)的并發(fā)模型”冬念。許多并發(fā)語言結(jié)構(gòu)和工具都是設(shè)計(jì)成支持這種并發(fā)模型的。
然而牧挣,時(shí)至今日急前,并發(fā)架構(gòu)和設(shè)計(jì)已經(jīng)發(fā)生了變化。
共享狀態(tài)的并發(fā)模型引起許多很難解決的并發(fā)問題瀑构。所以裆针,一個(gè)可選的、被稱為“不共享”或“獨(dú)立狀態(tài)”的并發(fā)模型正在流行起來寺晌。在獨(dú)立狀態(tài)的并發(fā)模型中世吨,線程不共享任何對象和數(shù)據(jù)。這避免了很多共享狀態(tài)并發(fā)模型的并發(fā)訪問問題呻征。
全新且異步的“獨(dú)立狀態(tài)”平臺和工具耘婚,如Netty、Vert.x 和 Play / Akka 和 Qbit怕犁,涌現(xiàn)了。全新的非阻塞并發(fā)算法被發(fā)布己莺,并且全新的非阻塞工具如LMax Disrupter已經(jīng)加入到我們的工具當(dāng)中奏甫。
還有Java 7的Fork / Join 框架、Java 8的集合流API凌受。