摘要:并發(fā)表示在一段時(shí)間內(nèi)有多個(gè)動(dòng)作存在。并發(fā)帶來(lái)的問(wèn)題在享受并發(fā)編程帶來(lái)的高性能高吞吐量的同時(shí),也會(huì)因?yàn)椴l(fā)編程帶來(lái)一些意想不到弊端。并發(fā)過(guò)程中多線程之間的切換調(diào)度,上下文的保存恢復(fù)等都會(huì)帶來(lái)額外的線程切換開(kāi)銷。
0x01 什么是并發(fā)
要理解并發(fā)首選我們來(lái)區(qū)分下并發(fā)和并行的概念。
并發(fā):表示在一段時(shí)間內(nèi)有多個(gè)動(dòng)作存在。
并行:表示在同一時(shí)間點(diǎn)有多個(gè)動(dòng)作同時(shí)存在。
例如:
此刻我正在寫(xiě)博客,但是我寫(xiě)著寫(xiě)著停下來(lái)吃一下東西(菠蘿片)再寫(xiě)、再吃。這兩個(gè)動(dòng)作在一段時(shí)間內(nèi)都在發(fā)生著,這可以理解為并發(fā)。
另一方面我在寫(xiě)這個(gè)博客的同時(shí)我在聽(tīng)音樂(lè)。那么同時(shí)存在的兩個(gè)動(dòng)作(寫(xiě)博客、聽(tīng)音樂(lè))是同時(shí)在發(fā)生的這就是所謂的并行。
從上面兩個(gè)概念明顯可以感受到并發(fā)是包含并行操作。所以我們通常說(shuō)的并發(fā)編程對(duì)于cpu來(lái)說(shuō)有可能是并發(fā)的在執(zhí)行也有可能是交替的在執(zhí)行。
說(shuō)到這里你可能會(huì)問(wèn)為什么我們需要并發(fā)編程?
在求解單個(gè)問(wèn)題的時(shí)候凡是涉及多個(gè)執(zhí)行流程的編程模式都叫并發(fā)編程。
硬件的發(fā)展推動(dòng)軟件的進(jìn)度,多核時(shí)代的到來(lái)
應(yīng)用系統(tǒng)對(duì)性能和吞吐量的苛刻要求
大數(shù)據(jù)時(shí)代的到來(lái)
移動(dòng)互聯(lián)網(wǎng)、云計(jì)算對(duì)計(jì)算體系的沖擊
0x03 并發(fā)編程方式Java:多進(jìn)程/多線程的并發(fā)實(shí)現(xiàn)方式
Go:協(xié)程--用戶態(tài)實(shí)現(xiàn)的多線程方式(goroutine)
Java并發(fā)模型在介紹java并發(fā)模型前我們來(lái)介紹下系統(tǒng)對(duì)多線程的實(shí)現(xiàn)方式。系統(tǒng)支持用戶態(tài)線程和內(nèi)核態(tài)兩種線程的實(shí)現(xiàn)方式,內(nèi)核態(tài)線程是cpu去調(diào)度的最小單位,所以這牽涉到用戶態(tài)線程和內(nèi)核態(tài)線程之間的映射關(guān)系,用戶態(tài)線程:內(nèi)核態(tài)線程 = 1:1 、 N:1 、 M:N。
1:1 這種映射關(guān)系充分利用多核的優(yōu)勢(shì),但是這種方式在用戶態(tài)進(jìn)行線程切換的過(guò)程中都會(huì)涉及到內(nèi)核態(tài)線程之間的切換,切換開(kāi)銷大。(主要涉及內(nèi)核線程運(yùn)行時(shí)上下文的保存與恢復(fù))
N:1 沒(méi)法充分利用多核的優(yōu)勢(shì),但是這種由于是用戶態(tài)的內(nèi)存切換不涉及內(nèi)核態(tài)線程之間的切換所以這種映射關(guān)系在線程之間切換代價(jià)小。
M:N 這種是上面兩種映射關(guān)系的結(jié)合體,集合了上面兩種映射關(guān)系的優(yōu)勢(shì),但是這也增加了線程之間這種映射關(guān)系的調(diào)度復(fù)雜度。
Java的并發(fā)編程模式是通過(guò)1:1這種映射關(guān)系來(lái)實(shí)現(xiàn)線程之間的并發(fā)調(diào)度。
Go并發(fā)模型Go的并發(fā)模式是通過(guò)M:N這種方式來(lái)實(shí)現(xiàn)并發(fā)調(diào)度的。
Go調(diào)度器中有三種重要結(jié)構(gòu):M(posix thread)、P(調(diào)度上下文,一般數(shù)量設(shè)置為和機(jī)器內(nèi)核數(shù)相同,這樣能充分發(fā)揮機(jī)器的并發(fā)性能)、G(goroutine)。
一個(gè)調(diào)度上下文可以包含多個(gè)Goroutine,多個(gè)上下文所以可以所有的Goroutine都能并發(fā)的運(yùn)行在CPU的多核上面。
如果有Goroutine發(fā)現(xiàn)找不到調(diào)度上下文,就會(huì)被放到global runqueue中,等清閑的調(diào)度上下文來(lái)?yè)迫∷M(jìn)行調(diào)度。
如果調(diào)度上下文上面掛載的所有Goroutine都已經(jīng)執(zhí)行完畢,此時(shí)他會(huì)去global runqueue中獲取Goroutine,如果發(fā)現(xiàn)此時(shí)沒(méi)有獲取到,則會(huì)去別的調(diào)度上文中搶Goroutine,一般一次搶都是搶此時(shí)被搶調(diào)度上下文的一半Goroutine,確保充分利用M去被多核調(diào)度。
在享受并發(fā)編程帶來(lái)的高性能、高吞吐量的同時(shí),也會(huì)因?yàn)椴l(fā)編程帶來(lái)一些意想不到弊端。
資源的消耗,要管理這么多用戶線程、內(nèi)核線程、用戶線程內(nèi)核線程之間的切換調(diào)度,上下文等等這些都是由于引用了并發(fā)編程所帶來(lái)的額外消耗。
并發(fā)過(guò)程中多線程之間的切換調(diào)度,上下文的保存恢復(fù)等都會(huì)帶來(lái)額外的線程切換開(kāi)銷。
編碼、測(cè)試的復(fù)雜性。和我們生活中的例子很相像,三五個(gè)人一起出去活動(dòng)很容易把控,如果帶著幾十、上百人的團(tuán)隊(duì)出去活動(dòng)這些都會(huì)帶來(lái)額外的管理上的開(kāi)銷。
真的是有陽(yáng)關(guān)的地方就有黑暗啊!
上面這些都是我們沒(méi)法避免的一些問(wèn)題,要引用并發(fā)編程必然會(huì)要付出點(diǎn)額外的代價(jià)才行。但是并發(fā)編程還帶來(lái)了一個(gè)不能忽視的問(wèn)題,線程之間對(duì)同一資源的競(jìng)爭(zhēng)訪問(wèn),造成內(nèi)存對(duì)象狀態(tài)和自己的想象千差萬(wàn)別。
java線程對(duì)內(nèi)存的理解分為兩部分:線程工作內(nèi)存(每個(gè)線程獨(dú)有的)、共享內(nèi)存也叫主內(nèi)存(所有的線程所共有的),下面是java線程對(duì)內(nèi)存中Count對(duì)象的一次修改操作。
從主線程中讀取Count對(duì)象放入線程工作內(nèi)存,后面的讀取修改都在線程工作內(nèi)存中,最后(更新到主內(nèi)存的時(shí)間不是確定的,可能會(huì)插入別的操作在store、write之間)更新到主內(nèi)存中。所有的上述操作都是順序執(zhí)行的,但是不保證連續(xù)執(zhí)行。
volatile變量、synchronized塊執(zhí)行結(jié)束后能保證每次去更新的值都會(huì)立即寫(xiě)入到主內(nèi)存中。
volatile變量很多人會(huì)認(rèn)為這樣就是線程安全的,但是通過(guò)上面我們可以看到如果兩個(gè)線程同時(shí)去讀了一個(gè)volatile變量,最后一前一后更新到主內(nèi)存中,這樣也會(huì)出現(xiàn)寫(xiě)丟失的情況,所以volatile不能保證線程安全。
1) 定義線程池
private static final ExecutorService executor = Executors.newFixedThreadPool(20);
2)定義并發(fā)服務(wù)
CompletionServicecompletionService = new ExecutorCompletionService (executor);
3)提交并發(fā)任務(wù)
completionService.submit(new Callable() { @Override public void call() throws Exception { return ; } });
4)等待并發(fā)結(jié)果
for (int i = 0; i < taskSize; ++i) { Futurefuture = completionService.poll(TIME_OUT, TimeUnit.SECONDS); Result result = future.get(); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/70030.html
摘要:所有示例代碼請(qǐng)見(jiàn)下載于基本概念并發(fā)同時(shí)擁有兩個(gè)或者多個(gè)線程,如果程序在單核處理器上運(yùn)行多個(gè)線程將交替地?fù)Q入或者換出內(nèi)存這些線程是同時(shí)存在的,每個(gè)線程都處于執(zhí)行過(guò)程中的某個(gè)狀態(tài),如果運(yùn)行在多核處理器上此時(shí),程序中的每個(gè)線程都 所有示例代碼,請(qǐng)見(jiàn)/下載于 https://github.com/Wasabi1234... showImg(https://upload-images.jians...
摘要:比如需要用多線程或分布式集群統(tǒng)計(jì)一堆用戶的相關(guān)統(tǒng)計(jì)值,由于用戶的統(tǒng)計(jì)值是共享數(shù)據(jù),因此需要保證線程安全。如果類是無(wú)狀態(tài)的,那它永遠(yuǎn)是線程安全的。參考探索并發(fā)編程二寫(xiě)線程安全的代碼 線程安全類 保證類線程安全的措施: 不共享線程間的變量; 設(shè)置屬性變量為不可變變量; 每個(gè)共享的可變變量都使用一個(gè)確定的鎖保護(hù); 保證線程安全的思路: 1. 通過(guò)架構(gòu)設(shè)計(jì) 通過(guò)上層的架構(gòu)設(shè)計(jì)和業(yè)務(wù)分析來(lái)避...
摘要:精讀前端可以從多個(gè)角度理解,比如規(guī)范框架語(yǔ)言社區(qū)場(chǎng)景以及整條研發(fā)鏈路。同是前端未來(lái)展望,不同的文章側(cè)重的格局不同,兩個(gè)標(biāo)題相同的文章內(nèi)容可能大相徑庭。作為使用者,現(xiàn)在和未來(lái)的主流可能都是微軟系,畢竟微軟在操作系統(tǒng)方面人才儲(chǔ)備和經(jīng)驗(yàn)積累很多。 1. 引言 前端展望的文章越來(lái)越不好寫(xiě)了,隨著前端發(fā)展的深入,需要擁有非常寬廣的視野與格局才能看清前端的未來(lái)。 筆者根據(jù)自身經(jīng)驗(yàn),結(jié)合下面幾篇文章...
摘要:函數(shù)式編程與面向?qū)ο缶幊叹幊痰谋举|(zhì)之劍目錄編程的本質(zhì)讀到兩篇文章寫(xiě)的不錯(cuò)綜合摘錄一下復(fù)合是編程的本質(zhì)函數(shù)式程序員在洞察問(wèn)題方面會(huì)遵循一個(gè)奇特的路線。在面向?qū)ο缶幊讨校惢蚪涌诘穆暶骶褪潜砻妗? 函數(shù)式編程與面向?qū)ο缶幊蘙5]:編程的本質(zhì) 之劍 2016.5.6 01:26:31 編程的本質(zhì) 讀到兩篇文章,寫(xiě)的不錯(cuò), 綜合摘錄一下 復(fù)合是編程的本質(zhì) 函數(shù)式程序員在洞察問(wèn)題方面會(huì)遵循...
閱讀 2357·2021-11-16 11:52
閱讀 2334·2021-11-11 16:55
閱讀 761·2021-09-02 15:41
閱讀 2993·2019-08-30 15:54
閱讀 3150·2019-08-30 15:54
閱讀 2259·2019-08-29 15:39
閱讀 1516·2019-08-29 15:18
閱讀 979·2019-08-29 13:00