多線程并發(fā)總結(jié)(八)--Java內(nèi)存模型

1. 計算機(jī)原理

? Java內(nèi)存模型即Java Memory Model店茶,簡稱JMM建蹄。JMM定義了Java 虛擬機(jī)(JVM)在計算機(jī)內(nèi)存(RAM)中的工作方式茴扁。JVM是整個計算機(jī)虛擬模型登下,所以JMM是隸屬于JVM的卓练。Java1.5版本對其進(jìn)行了重構(gòu),現(xiàn)在的Java仍沿用了Java1.5的版本晒哄。

根據(jù)《Jeff Dean在Google全體工程大會的報告》我們可以看到

計算機(jī)執(zhí)行不同操作耗時.png

計算機(jī)在做一些我們平時的基本操作時泉蝌,需要的響應(yīng)時間是不一樣的。

2. Java內(nèi)存模型

? 從抽象的角度來看揩晴,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲在主內(nèi)存(Main Memory)中勋陪,每個線程都有一個私有的本地內(nèi)存(Local Memory),本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本硫兰。本地內(nèi)存是JMM的一個抽象概念诅愚,并不真實存在。它涵蓋了緩存劫映、寫緩沖區(qū)违孝、寄存器以及其他的硬件和編譯器優(yōu)化。

JMM模型.png
2.1 可見性

? 可見性是指當(dāng)多個線程訪問同一個變量時泳赋,一個線程修改了這個變量的值雌桑,其他線程能夠立即看得到修改的值。

? 由于線程對變量的所有操作都必須在工作內(nèi)存中進(jìn)行祖今,而不能直接讀寫主內(nèi)存中的變量校坑,那么對于共享變量V拣技,它們首先是在自己的工作內(nèi)存,之后再同步到主內(nèi)存耍目「嘟铮可是并不會及時的刷到主存中,而是會有一定時間差邪驮。很明顯莫辨,這個時候線程 A 對變量 V 的操作對于線程 B 而言就不具備可見性了 。

? 要解決共享對象可見性這個問題毅访,我們可以使用volatile關(guān)鍵字或者是加鎖沮榜。

2.2 原子性

? 原子性:即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷,要么就都不執(zhí)行喻粹。

? 我們都知道CPU資源的分配都是以線程為單位的,并且是分時調(diào)用,操作系統(tǒng)允許某個進(jìn)程執(zhí)行一小段時間蟆融,例如 50 毫秒,過了 50 毫秒操作系統(tǒng)就會重新選擇一個進(jìn)程來執(zhí)行(我們稱為“任務(wù)切換”)磷斧,這個 50 毫秒稱為“時間片”振愿。而任務(wù)的切換大多數(shù)是在時間片段結(jié)束以后,

? 那么線程切換為什么會帶來bug呢捷犹?因為操作系統(tǒng)做任務(wù)切換弛饭,可以發(fā)生在任何一條CPU 指令執(zhí)行完!注意萍歉,是 CPU 指令,而不是高級語言里的一條語句侣颂。比如count++,在java里就是一句話枪孩,但高級語言里一條語句往往需要多條 CPU 指令完成憔晒。其實count++包含了三個CPU指令!

3.volatile詳解

3.1 volatile特性

? 可見性蔑舞。對一個volatile變量的讀拒担,總是能看到(任意線程)對這個volatile變量最后的寫入。

? 原子性:對任意單個volatile變量的讀/寫具有原子性攻询,但類似于volatile++這種復(fù)合操作不具有原子性从撼。例如:volatile雖然能保證執(zhí)行完及時把變量刷到主內(nèi)存中,但對于count++這種非原子性钧栖、多指令的情況低零,由于線程切換,線程A剛把count=0加載到工作內(nèi)存拯杠,線程B就可以開始工作了掏婶,這樣就會導(dǎo)致線程A和B執(zhí)行完的結(jié)果都是1,都寫到主內(nèi)存中潭陪,主內(nèi)存的值還是1不是2雄妥。

? 抑制重排序:有volatile修飾的變量最蕾,賦值后多執(zhí)行了一個“l(fā)oad addl $0x0, (%esp)”操作,這個操作相當(dāng)于一個內(nèi)存屏障(指令重排序時不能把后面的指令重排序到內(nèi)存屏障之前的位置)茎芭。

? 指令重排序:處理器為了提高程序運行效率揖膜,可能會對輸入代碼進(jìn)行優(yōu)化适揉,它不保證程序中各個語句的執(zhí)行先后順序同代碼中的順序一致叠聋,但是它會保證程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是一致的。

3.2 volatile的實現(xiàn)原理

? 為了探究Volatile的底層實現(xiàn)原理呻此,我們需要先將java代碼編程成字節(jié)碼宿百,然后通過java工具看匯編代碼就可以知道底層原理趁仙。下面來看看

public class TestVolatile {

    private static volatile int i = 0;

    public static void main(String[] args) {
    }
}

使用javap -c 文件名 或者AndroidStudio的ASM插件看字節(jié)碼

static <clinit>()V
   L0
    LINENUMBER 8 L0
    ICONST_0
    // 下面這行字節(jié)碼無論是否使用volatile修飾,都是一樣的
    PUTSTATIC com/example/threadtest/volatilesynchronized/TestVolatile.i : I
    RETURN
    MAXSTACK = 1
    MAXLOCALS = 0

然后繼續(xù)往下探索

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly TestVolatile > 1.txt

查看匯編碼

    Line 2521:   0x000000000356fecc: lock cmpxchg qword ptr [rdx],rdi
    Line 2528:   0x000000000356feed: lock cmpxchg qword ptr [rdx],rdi
    Line 2534:   0x000000000356ff0b: lock add dword ptr [rsp],0h ;*putstatic
    Line 2538:   0x000000000356ff1a: lock cmpxchg qword ptr [rdx],rsi
    Line 2816:   0x0000000003570338: lock cmpxchg qword ptr [rdi],rsi
    Line 2941:   0x0000000003570479: lock cmpxchg qword ptr [rsi],rdi

可以看到

匯編指令中會有很多加“l(fā)ock”前綴的指令垦页。

加入volatile關(guān)鍵字和沒有加入volatile關(guān)鍵字時所生成的匯編代碼發(fā)現(xiàn)雀费,加入volatile關(guān)鍵字時,會多出一個lock前綴指令痊焊。我們發(fā)現(xiàn)盏袄,volatile變量在字節(jié)碼級別沒有任何區(qū)別,在匯編級別使用了lock指令前綴薄啥。

lock是一個指令前綴辕羽,Intel的手冊上對其的解釋是:

Causes the processor's LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal insures that the processor has exclusive use of any shared memory while the signal is asserted.

? 簡單理解也就是說,lock后就是一個原子操作垄惧。原子操作是指不會被線程調(diào)度機(jī)制打斷的操作刁愿;這種操作一旦開始,就一直運行到結(jié)束到逊,中間不會有任何 context switch (切換到另一個線程)铣口。

? 當(dāng)使用 LOCK 指令前綴時,它會使 CPU 宣告一個 LOCK# 信號觉壶,這樣就能確保在多處理器系統(tǒng)或多線程競爭的環(huán)境下互斥地使用這個內(nèi)存地址脑题。當(dāng)指令執(zhí)行完畢,這個鎖定動作也就會消失铜靶。

? 乍一看是不是感覺和Java的synchronized鎖叔遂。但volatile底層使用多核處理器實現(xiàn)的lock指令,更底層旷坦,消耗代價更小掏熬。

? 因此有人將Java的synchronized看作重量級的鎖,而volatile看作輕量級的鎖 并不是全無道理秒梅。

? lock前綴指令其實就相當(dāng)于一個內(nèi)存屏障旗芬。內(nèi)存屏障是一組CPU處理指令,用來實現(xiàn)對內(nèi)存操作的順序限制捆蜀。volatile的底層就是通過內(nèi)存屏障來實現(xiàn)的疮丛。

? 編譯器和執(zhí)行器 可以在保證輸出結(jié)果一樣的情況下對指令重排序幔嫂,使性能得到優(yōu)化。插入一個內(nèi)存屏障誊薄,相當(dāng)于告訴CPU和編譯器先于這個命令的必須先執(zhí)行履恩,后于這個命令的必須后執(zhí)行。

? 內(nèi)存屏障另一個作用是強(qiáng)制更新一次不同CPU的緩存呢蔫。例如切心,一個寫屏障會把這個屏障前寫入的數(shù)據(jù)刷新到緩存,這樣任何試圖讀取該數(shù)據(jù)的線程將得到最新值片吊,而不用考慮到底是被哪個cpu核心或者哪個CPU執(zhí)行的绽昏。這正是volatile實現(xiàn)內(nèi)存可見性的基礎(chǔ)。

3.3 synchronized的實現(xiàn)原理

? Synchronized在JVM里的實現(xiàn)都是基于進(jìn)入和退出Monitor對象來實現(xiàn)方法同步和代碼塊同步俏脊,雖然具體實現(xiàn)細(xì)節(jié)不一樣全谤,但是都可以通過成對的MonitorEnter和MonitorExit指令來實現(xiàn)。

// 測試代碼
public class Test {

    private static volatile int i = 0;
    private final Object lock = new Object();

    public void a() {
        synchronized (lock) {
            i++;
        }
    }

    public synchronized void b() {
        i++;
    }
}

? 對同步塊爷贫,MonitorEnter指令插入在同步代碼塊的開始位置认然,當(dāng)代碼執(zhí)行到該指令時,將會嘗試獲取該對象Monitor的所有權(quán)漫萄,即嘗試獲得該對象的鎖卷员,而monitorExit指令則插入在方法結(jié)束處和異常處,JVM保證每個MonitorEnter必須有對應(yīng)的MonitorExit卷胯。

public a()V
    ......
    
    DUP
    ASTORE 1
    MONITORENTER      // 同步塊開始
   L0
    LINENUMBER 15 L0
    GETSTATIC com/example/threadtest/volatilesynchronized/Test.i : I
    ICONST_1
    IADD
    PUTSTATIC com/example/threadtest/volatilesynchronized/Test.i : I
   L5
    LINENUMBER 16 L5
    ALOAD 1
    MONITOREXIT   // 同步塊結(jié)束
   L1
    GOTO L6
    
     ......

? 對同步方法子刮,從同步方法反編譯的結(jié)果來看威酒,方法的同步并沒有通過指令monitorenter和monitorexit來實現(xiàn)窑睁,相對于普通方法,其常量池中多了ACC_SYNCHRONIZED標(biāo)示符葵孤。

  // access flags 0x1    沒有synchronized修飾
  public a()V
  
  // access flags 0x21   synchronized修飾
  public synchronized b()V

? JVM就是根據(jù)該標(biāo)示符來實現(xiàn)方法的同步的:當(dāng)方法被調(diào)用時担钮,調(diào)用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置,如果設(shè)置了尤仍,執(zhí)行線程將先獲取monitor箫津,獲取成功之后才能執(zhí)行方法體,方法執(zhí)行完后再釋放monitor宰啦。在方法執(zhí)行期間苏遥,其他任何線程都無法再獲得同一個monitor對象。

? synchronized使用的鎖信息是存放在Java對象頭里面赡模。

對象頭.png

? 具體位置是對象頭里面的MarkWord田炭,MarkWord里默認(rèn)數(shù)據(jù)是存儲對象的HashCode等信息,

markword位定義.png

? 但是會隨著對象的運行改變而發(fā)生變化漓柑,不同的鎖狀態(tài)對應(yīng)著不同的記錄存儲方式

鎖狀態(tài).png

3.4 各種鎖

3.4.1.1 自旋鎖 (輕量級鎖)

? 自旋鎖原理非常簡單教硫,如果持有鎖的線程能在很短時間內(nèi)釋放鎖資源叨吮,那么那些等待競爭鎖的線程就不需要做內(nèi)核態(tài)和用戶態(tài)之間的切換進(jìn)入阻塞掛起狀態(tài),它們只需要等一等(自旋)瞬矩,等持有鎖的線程釋放鎖后即可立即獲取鎖茶鉴,這樣就避免用戶線程和內(nèi)核的切換的消耗。

3.4.1.2 自旋鎖的優(yōu)缺點

? 自旋鎖盡可能的減少線程的阻塞景用,這對于鎖的競爭不激烈涵叮,且占用鎖時間非常短的代碼塊來說性能能大幅度的提升,因為自旋的消耗會小于線程阻塞掛起操作的消耗伞插。

? 但是如果鎖的競爭激烈围肥,或者持有鎖的線程需要長時間占用鎖執(zhí)行同步塊,這時候就不適合使用自旋鎖了蜂怎,因為自旋鎖在獲取鎖前一直都是占用cpu做無用功穆刻。

3.4.1.3 自旋鎖時間閾值

? JVM對于自旋次數(shù)的選擇,jdk1.5默認(rèn)為10次杠步,在1.6引入了適應(yīng)性自旋鎖

氢伟,適應(yīng)性自旋鎖意味著自旋的時間不在是固定的了,而是由前一次在同一個鎖上的自旋時間以及鎖的擁有者的狀態(tài)來決定幽歼,基本認(rèn)為一個線程上下文切換的時間是最佳的一個時間朵锣。

? JDK1.6中-XX:+UseSpinning開啟自旋鎖; JDK1.7后甸私,去掉此參數(shù)诚些,由jvm控制

3.4.2.1 偏向鎖

? 研究人員經(jīng)過大量統(tǒng)計,發(fā)現(xiàn)一把鎖總是被同一線程競爭拿到皇型,所以就引入偏向鎖概念诬烹,當(dāng)在進(jìn)行CAS自旋拿鎖之前,先判斷自己是不是在競爭這把鎖弃鸦,如果是就直接執(zhí)行绞吁。

? 偏向鎖,顧名思義唬格,它會偏向于第一個訪問鎖的線程家破,如果在運行過程中,同步鎖只有一個線程訪問购岗,不存在多線程爭用的情況汰聋,則線程是不需要觸發(fā)同步的,減少加鎖/解鎖的一些CAS操作(比如等待隊列的一些CAS操作)喊积,這種情況下烹困,就會給線程加一個偏向鎖。 如果在運行過程中注服,遇到了其他線程搶占鎖韭邓,則持有偏向鎖的線程會被掛起措近,JVM會消除它身上的偏向鎖,將鎖恢復(fù)到標(biāo)準(zhǔn)的輕量級鎖女淑。它通過消除資源無競爭情況下的同步原語瞭郑,進(jìn)一步提高了程序的運行性能。

偏向鎖獲取過程:

  • 第一步鸭你,訪問Mark Word中偏向鎖的標(biāo)識是否設(shè)置成1屈张,鎖標(biāo)志位是否為01,確認(rèn)為可偏向狀態(tài)袱巨。

  • 第二步阁谆,如果為可偏向狀態(tài),則測試線程ID是否指向當(dāng)前線程愉老,如果是场绿,進(jìn)入步驟5,否則進(jìn)入步驟3嫉入。

  • 第三步焰盗,如果線程ID并未指向當(dāng)前線程,則通過CAS操作競爭鎖咒林。如果競爭成功熬拒,則將Mark Word中線程ID設(shè)置為當(dāng)前線程ID,然后執(zhí)行5垫竞;如果競爭失敗澎粟,執(zhí)行4。

  • 第四步欢瞪,如果CAS獲取偏向鎖失敗活烙,則表示有競爭。當(dāng)?shù)竭_(dá)全局安全點(safepoint)時獲得偏向鎖的線程被掛起引有,偏向鎖升級為輕量級鎖瓣颅,然后被阻塞在安全點的線程繼續(xù)往下執(zhí)行同步代碼倦逐。(撤銷偏向鎖的時候會導(dǎo)致stop the word)

  • 第五步譬正,執(zhí)行同步代碼。

偏向鎖的釋放:

? 偏向鎖的撤銷在上述第四步驟中有提到檬姥。偏向鎖只有遇到其他線程嘗試競爭偏向鎖時曾我,持有偏向鎖的線程才會釋放偏向鎖,線程不會主動去釋放偏向鎖健民。偏向鎖的撤銷抒巢,需要等待全局安全點(在這個時間點上沒有字節(jié)碼正在執(zhí)行),它會首先暫停擁有偏向鎖的線程秉犹,判斷鎖對象是否處于被鎖定狀態(tài)蛉谜,撤銷偏向鎖后恢復(fù)到未鎖定(標(biāo)志位為“01”)或輕量級鎖(標(biāo)志位為“00”)的狀態(tài)稚晚。

偏向鎖的適用場景

? 始終只有一個線程在執(zhí)行同步塊,在它沒有執(zhí)行完釋放鎖之前型诚,沒有其它線程去執(zhí)行同步塊客燕,在鎖無競爭的情況下使用,一旦有了競爭就升級為輕量級鎖狰贯,升級為輕量級鎖的時候需要撤銷偏向鎖也搓,撤銷偏向鎖的時候會導(dǎo)致stop the word操作;

? 在有鎖的競爭時涵紊,偏向鎖會多做很多額外操作傍妒,尤其是撤銷偏向所的時候會導(dǎo)致進(jìn)入安全點,安全點會導(dǎo)致stw摸柄,導(dǎo)致性能下降颤练,這種情況下應(yīng)當(dāng)禁用。

jvm開啟/關(guān)閉偏向鎖

開啟偏向鎖:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

關(guān)閉偏向鎖:-XX:-UseBiasedLocking

3.4.3 輕量級鎖

? 輕量級鎖是由偏向鎖升級來的驱负,偏向鎖運行在一個線程進(jìn)入同步塊的情況下昔案,當(dāng)?shù)诙€線程加入鎖爭用的時候,偏向鎖就會升級為輕量級鎖电媳;

輕量級鎖的加鎖過程:

? 在代碼進(jìn)入同步塊的時候踏揣,如果同步對象鎖狀態(tài)為無鎖狀態(tài)且不允許進(jìn)行偏向(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”)匾乓,虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間捞稿,用于存儲鎖對象目前的Mark Word的拷貝,官方稱之為 Displaced Mark Word拼缝。

? 拷貝成功后娱局,虛擬機(jī)將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針,并將Lock record里的owner指針指向object mark word咧七。如果更新成功衰齐,則執(zhí)行步驟4,否則執(zhí)行步驟5继阻。

  • 如果這個更新動作成功了耻涛,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標(biāo)志位設(shè)置為“00”瘟檩,即表示此對象處于輕量級鎖定狀態(tài)

  • 如果這個更新操作失敗了抹缕,虛擬機(jī)首先會檢查對象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖墨辛,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行卓研。否則說明多個線程競爭鎖,當(dāng)競爭線程嘗試占用輕量級鎖失敗多次之后,輕量級鎖就會膨脹為重量級鎖奏赘,重量級線程指針指向競爭線程寥闪,競爭線程也會阻塞,等待輕量級線程釋放鎖后喚醒他磨淌。鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”橙垢,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,后面等待鎖的線程也要進(jìn)入阻塞狀態(tài)伦糯。

3.4.4 不同鎖之間的區(qū)別

? JVM虛擬機(jī)對synchronized關(guān)鍵字的優(yōu)化引入了自適應(yīng)性CAS鎖柜某、偏向鎖和輕量級鎖機(jī)制。

對synchronized用不同鎖的優(yōu)化.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敛纲,一起剝皮案震驚了整個濱河市喂击,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淤翔,老刑警劉巖翰绊,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旁壮,居然都是意外死亡监嗜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門抡谐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裁奇,“玉大人,你說我怎么就攤上這事麦撵」舫Γ” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵免胃,是天一觀的道長音五。 經(jīng)常有香客問我,道長羔沙,這世上最難降的妖魔是什么躺涝? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮扼雏,結(jié)果婚禮上坚嗜,老公的妹妹穿的比我還像新娘。我一直安慰自己呢蛤,他們只是感情好惶傻,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著其障,像睡著了一般。 火紅的嫁衣襯著肌膚如雪涂佃。 梳的紋絲不亂的頭發(fā)上励翼,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天蜈敢,我揣著相機(jī)與錄音,去河邊找鬼汽抚。 笑死抓狭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的造烁。 我是一名探鬼主播否过,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惭蟋!你這毒婦竟也來了苗桂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤告组,失蹤者是張志新(化名)和其女友劉穎煤伟,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體木缝,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡便锨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了我碟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片放案。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖矫俺,靈堂內(nèi)的尸體忽然破棺而出卿叽,到底是詐尸還是另有隱情,我是刑警寧澤恳守,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布考婴,位于F島的核電站,受9級特大地震影響催烘,放射性物質(zhì)發(fā)生泄漏沥阱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一伊群、第九天 我趴在偏房一處隱蔽的房頂上張望考杉。 院中可真熱鬧,春花似錦舰始、人聲如沸崇棠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽枕稀。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間萎坷,已是汗流浹背凹联。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留哆档,地道東北人蔽挠。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像瓜浸,于是被迫代替她去往敵國和親澳淑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

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

  • 1.并發(fā)基礎(chǔ) 定義:一個cpu“同時”處理多個任務(wù)插佛,而多個線程都在爭取這個cpu資源 1.1 優(yōu)點 充分發(fā)揮多核C...
    smartzheng閱讀 984評論 0 0
  • 1.并發(fā)基礎(chǔ) 定義:一個cpu“同時”處理多個任務(wù)杠巡,而多個線程都在爭取這個cpu資源 1.1 優(yōu)點 充分發(fā)揮多核C...
    程序人生a閱讀 356評論 0 0
  • 1. 計算機(jī)系統(tǒng) 使用高速緩存來作為內(nèi)存與處理器之間的緩沖,將運算需要用到的數(shù)據(jù)復(fù)制到緩存中朗涩,讓計算能快速進(jìn)行忽孽;當(dāng)...
    AI喬治閱讀 540評論 0 12
  • 前言目前CPU的運算速度已經(jīng)達(dá)到了百億次每秒,所以為了提高生產(chǎn)率和高效地完成任務(wù)谢床,基本上都采用多線程和并發(fā)的運作方...
    Lemonrel閱讀 556評論 0 0
  • 久違的晴天兄一,家長會。 家長大會開好到教室時识腿,離放學(xué)已經(jīng)沒多少時間了出革。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評論 16 22