并發(fā)的前提條件
并發(fā)問(wèn)題發(fā)生的前提條件一定是資源共享,這里的資源一般指的是數(shù)據(jù),共享指的是多線(xiàn)程之間共享。
也就是只有在多線(xiàn)程共享資源的情況下才可能產(chǎn)生并發(fā)問(wèn)題,這是并發(fā)問(wèn)題產(chǎn)生的前提條件,在這個(gè)條件下,有可能產(chǎn)生并發(fā)問(wèn)題,那么并發(fā)問(wèn)題的根源究竟是什么呢?
CPU操作數(shù)據(jù)的基本機(jī)制
前面提到了并發(fā)的前提條件是數(shù)據(jù)共享,想了解并發(fā)問(wèn)題的根源就需要知道CPU操作數(shù)據(jù)的基本原理;
數(shù)據(jù)存儲(chǔ)包括這幾個(gè)位置:磁盤(pán)、內(nèi)存、緩存、寄存器;
寄存器可以認(rèn)為是CPU的一部分,所以有的地方并沒(méi)有將CPU和寄存器拆分講解,通常來(lái)講只需要知道CPU運(yùn)算時(shí)都是從寄存器取數(shù)據(jù),運(yùn)算完成后再放回寄存器即可,CPU和寄存器之間沒(méi)有其他任何中介。
緩存是CPU與寄存器之外的一層存儲(chǔ),但也是每一個(gè)CPU獨(dú)立占有的一塊內(nèi)存區(qū)域,各個(gè)CPU緩存之間數(shù)據(jù)不可以共享。
內(nèi)存是程序運(yùn)行時(shí)數(shù)據(jù)的主要存放區(qū)域,內(nèi)存數(shù)據(jù)是共享的,一般來(lái)講,各個(gè)CPU都可以訪(fǎng)問(wèn)內(nèi)存中的數(shù)據(jù);
磁盤(pán),數(shù)據(jù)最終持久化的存儲(chǔ);
CPU操作數(shù)據(jù)的流程一般是先由磁盤(pán)讀到內(nèi)存,再?gòu)膬?nèi)存讀到緩存,再由緩存到寄存器進(jìn)行運(yùn)算;運(yùn)算之后的結(jié)果直接寫(xiě)入寄存器,然后刷新到緩存,再刷新到內(nèi)存,最后寫(xiě)入磁盤(pán);
程序數(shù)據(jù)流圖
并發(fā)問(wèn)題的源頭
了解了CPU運(yùn)行機(jī)制之后,下面說(shuō)并發(fā)問(wèn)題的根源,主要是由于數(shù)據(jù)可見(jiàn)性、操作原子性、操作有序性這三個(gè)原因?qū)е碌模?/p>
什么是數(shù)據(jù)可見(jiàn)性?
通俗點(diǎn)來(lái)說(shuō)就是CPU看到的數(shù)據(jù)并不是最新的數(shù)據(jù),CPU讀取數(shù)據(jù)是優(yōu)先從緩存中讀取,如果緩存中存在就使用緩存中的數(shù)據(jù),假如數(shù)據(jù)被另一個(gè)CPU改變了,這時(shí)其他CPU中緩存數(shù)據(jù)就可能與內(nèi)存中的數(shù)據(jù)不一致,也就是CPU沒(méi)有看到并使用最新的數(shù)據(jù),導(dǎo)致程序執(zhí)行結(jié)果異常。
什么是操作原子性?
同一個(gè)CPU可以交替執(zhí)行多個(gè)線(xiàn)程,不太了解的讀者可以初步學(xué)習(xí)一下CPU時(shí)間片與線(xiàn)程調(diào)度的基本知識(shí)。
在同一個(gè)CPU,交替執(zhí)行多個(gè)線(xiàn)程的時(shí)候,就可能出現(xiàn)線(xiàn)程中斷,并且在中斷過(guò)程中受其他線(xiàn)程影響而導(dǎo)致中斷的線(xiàn)程恢復(fù)之后,執(zhí)行邏輯異常。
比如:a線(xiàn)程執(zhí)行count = count + 1操作,b線(xiàn)程也執(zhí)行相同的操作;當(dāng)a線(xiàn)程讀取到count的值,并進(jìn)行加1計(jì)算之后,還沒(méi)寫(xiě)回到內(nèi)存之前被中斷,b線(xiàn)程完全執(zhí)行了count = count + 1,count的值得到更新;這時(shí)a線(xiàn)程恢復(fù)(并不會(huì)重新讀取并計(jì)算),將之前計(jì)算的值寫(xiě)回到緩存,導(dǎo)致count本來(lái)應(yīng)該執(zhí)行兩次加1,但最終結(jié)果只加了一次1;
什么是操作有序性?
有序性指的是CPU執(zhí)行代碼的順序和程序開(kāi)發(fā)者定義的順序不一致?為什么還會(huì)不一致呢?
編譯器在將高級(jí)開(kāi)發(fā)語(yǔ)言編譯成計(jì)算機(jī)指令的時(shí)候,出于性能優(yōu)化,可能會(huì)對(duì)代碼執(zhí)行重排序,CPU在執(zhí)行指令的時(shí)候,也可能對(duì)代碼重排序;當(dāng)然重排序的前提是在單線(xiàn)程條件下的語(yǔ)義不變性,但不能保證多線(xiàn)程條件下語(yǔ)義也相同。
Java單例模式中的雙重校驗(yàn)鎖,單例變量為什么要聲明為volatile,就是為了解決指令重排序帶來(lái)的問(wèn)題,我們?cè)谙乱徽鹿?jié)進(jìn)行詳細(xì)講解。感興趣的也可以自行查閱資料學(xué)習(xí)。
并發(fā)問(wèn)題的解決方案
并發(fā)問(wèn)題不是Java語(yǔ)言特有的,而是計(jì)算機(jī)運(yùn)行原理與操作系統(tǒng)帶來(lái)的,那么從計(jì)算機(jī)與操作系統(tǒng)層面來(lái)看,它們都提供了哪些解決方案來(lái)避免數(shù)據(jù)可見(jiàn)性、程序原子性、操作有序性的保障呢?Java語(yǔ)言又是如何對(duì)這些方案進(jìn)行封裝的呢?開(kāi)發(fā)者有哪些手段可以解決這些問(wèn)題呢?
-
JAVA
+關(guān)注
關(guān)注
20文章
2984瀏覽量
106841 -
線(xiàn)程
+關(guān)注
關(guān)注
0文章
507瀏覽量
20070 -
進(jìn)程
+關(guān)注
關(guān)注
0文章
206瀏覽量
14211
發(fā)布評(píng)論請(qǐng)先 登錄
進(jìn)程、線(xiàn)程、協(xié)程傻傻分不清?一文帶你徹底扒光它們的\"底褲\"!
請(qǐng)問(wèn)如何在Python中實(shí)現(xiàn)多線(xiàn)程與多進(jìn)程的協(xié)作?
摩爾線(xiàn)程支持DeepSeek開(kāi)源通信庫(kù)DeepEP和并行算法DualPipe
socket 多線(xiàn)程編程實(shí)現(xiàn)方法
加鎖失效,非鎖之過(guò),加之錯(cuò)也

一文搞懂Linux進(jìn)程的睡眠和喚醒
go語(yǔ)言如何解決并發(fā)問(wèn)題

Python中多線(xiàn)程和多進(jìn)程的區(qū)別

CPU線(xiàn)程和程序線(xiàn)程的區(qū)別
三十分鐘入門(mén)基礎(chǔ)Go Java小子版

華納云:java web和java有什么區(qū)別java web和java有什么區(qū)別

從多線(xiàn)程設(shè)計(jì)模式到對(duì) CompletableFuture 的應(yīng)用

探索虛擬線(xiàn)程:原理與實(shí)現(xiàn)

動(dòng)態(tài)線(xiàn)程池思想學(xué)習(xí)及實(shí)踐

一句話(huà)讓你理解線(xiàn)程和進(jìn)程

評(píng)論