blp模型 上读下写_Java高并发编程(三):Java内存模型
1 Java內(nèi)存模型的基礎(chǔ)
在并發(fā)編程里,需要處理兩個(gè)問題:
通信指的是線程之間以何種機(jī)制來交換信息。在命令式編程里中,線程之間的通信機(jī)制有兩種:共享內(nèi)存和消息傳遞。 Java的并發(fā)采用的是共享內(nèi)存模型。
1.1 Java內(nèi)存模型的抽象結(jié)構(gòu)
Java線程之間的通信由Java內(nèi)存模型(JMM)控制,JMM決定一個(gè)線程對(duì)共享變量的寫入何時(shí)對(duì)另一個(gè)線程可見。線程之間的共享變量存儲(chǔ)在主內(nèi)存中,每個(gè)線程都有一個(gè)私有的本地內(nèi)存,本地內(nèi)存中存儲(chǔ)了該線程以讀、寫共享變量的副本。
從圖中可以看到,如果線程A和線程B之間要通信的話,必須經(jīng)歷如下的2步:
如圖,假設(shè)初始時(shí),本地內(nèi)存A、B以及主內(nèi)存中X均為0,線程A在執(zhí)行時(shí),把更新后的x值(假設(shè)為1)臨時(shí)存放在自己的本地內(nèi)存A中。當(dāng)線程A和現(xiàn)場(chǎng)B需要通信時(shí),線程A首先會(huì)把自己的本次內(nèi)存中修改的x值刷新到主內(nèi)存中,此時(shí)主內(nèi)存中的x值變成了1。隨后線程B到主內(nèi)存中去讀取線程A更新后的x值,此時(shí)線程B的本地內(nèi)存中的x值也變成了1。
1.2 從源代碼到指令序列的重排序
我們了解了Java內(nèi)存模型的抽象結(jié)構(gòu)之后,下面我們來簡單聊一下一段Java代碼到編譯成字節(jié)碼之后,再到最后處理器運(yùn)行時(shí)進(jìn)行指令序列的重排序過程。
在執(zhí)行程序時(shí),為了提高性能,編譯器和處理器常常會(huì)對(duì)指令做重排序。重排序分為以下3種:
從Java源代碼到最終實(shí)際執(zhí)行的指令序列,會(huì)分別經(jīng)歷下面3種重排序,如下圖:
1.3 并發(fā)編程模型的分類
為了保證內(nèi)存可見性,java編譯器在生成指令序列的適當(dāng)位置會(huì)插入內(nèi)存屏障指令來禁止特定類型的處理器重排序。JMM把內(nèi)存屏障指令分為4類:
1.4 happens-before簡介
如果一個(gè)操作執(zhí)行的結(jié)果需要對(duì)另一個(gè)操作可見,那么這兩個(gè)操作之間必須要存在happens-before關(guān)系。這兩個(gè)操作可以在同一個(gè)線程中,也可以在不同的線程中。
2 指令重排序
重排序是指編譯器和處理器為了優(yōu)化程序性能而對(duì)指令序列進(jìn)行重新排序的一種手段。
2.1 數(shù)據(jù)依賴性
如果兩個(gè)操作訪問同一個(gè)變量,且這兩個(gè)操作中有一個(gè)為寫操作,此時(shí)這兩個(gè)操作之間就存在數(shù)據(jù)依賴性。
數(shù)據(jù)依賴性分為以下三種:
- 寫后讀
- 寫后寫
- 讀后寫
這里所說的數(shù)據(jù)依賴性僅針對(duì)于單個(gè)處理器中執(zhí)行的指令序列和單個(gè)線程中執(zhí)行的操作
2.2 as-if-serial語義
as-if-serial的意思是,不管怎么重排序,(單線程)程序的執(zhí)行結(jié)果不能被改變。 所以為了遵守as-if-serial語義,編譯器和處理器不會(huì)對(duì)存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因?yàn)橹嘏判驎?huì)改變執(zhí)行的結(jié)果。反之,如果不存在數(shù)據(jù)依賴關(guān)系,這些操作是可以被編譯器和處理器重排序的。
2.3 程序順序規(guī)則
在計(jì)算機(jī)中,軟件技術(shù)和硬件技術(shù)有一個(gè)共同的目標(biāo):在不改變程序執(zhí)行結(jié)果的前提下,盡可能提高并行度。
3. 順序一致性
順序一致性模型是一個(gè)被計(jì)算機(jī)科學(xué)家理想化了的理想?yún)⒖寄P汀?/p>
3.1 數(shù)據(jù)競(jìng)爭與順序一致性
JMM對(duì)正確同步的多線程程序的內(nèi)存一致性做了如下的保證:
順序一致性定義:如果程序是正確同步的,程序的執(zhí)行將具有順序一致性(Sequentially Consistent)—— 即程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同。
3.2 順序一致性內(nèi)存模型
順序一致性模型具有以下兩大特性:
在概念上,順序一致性模型有一個(gè)單一的全局內(nèi)存,這個(gè)內(nèi)存通過一個(gè)左右擺動(dòng)的開關(guān)可以連接到任意一個(gè)線程上,同時(shí)每一個(gè)線程都必須按照程序的順序來執(zhí)行內(nèi)存讀/內(nèi)存寫操作。
3.3 未同步程序的執(zhí)行特性
JMM不保證未同步程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果一致。未同步程序在JMM內(nèi)存模型和順序一致性模型中存在以下幾個(gè)差異:
在計(jì)算機(jī)中,數(shù)據(jù)通過總線在處理器和內(nèi)存之間傳遞。每次處理器和內(nèi)存之間的數(shù)據(jù)傳遞都是通過一系列步驟來完成的,這一系列步驟稱為總線事務(wù)(Bus Transaction)。總線事務(wù)包括讀事務(wù)(Read Transaction)和寫事務(wù)(Write Transaction)。總線處理具有總線鎖定來同步對(duì)總線事務(wù)操作,在處理器執(zhí)行總線事務(wù)期間,總線會(huì)禁止其他的處理器和I/O設(shè)備執(zhí)行內(nèi)存的讀/寫操作。
總線的這些工作機(jī)制可以把所有處理器對(duì)內(nèi)存的訪問以串行化的方式來執(zhí)行。在任意時(shí)間,最多只有一個(gè)處理器可以訪問內(nèi)存。這個(gè)特性確保了單個(gè)總線事務(wù)之中的內(nèi)存讀/寫操作具有原子性。
那么為什么JMM不保證對(duì)64位的long性和double型變量的寫操作具有原子性?
在一些32位的處理器上,如果要求對(duì)64位的數(shù)據(jù)寫操作具有原子性,會(huì)有較大的開銷。當(dāng)JMM在這種處理器上運(yùn)行時(shí),可能會(huì)把一個(gè)64位long/double類型的變量的寫操作拆分為兩個(gè)32位的寫操作進(jìn)行執(zhí)行。這兩個(gè)32位的寫操作可能會(huì)被分配到不同的總線事務(wù)中執(zhí)行,此時(shí)對(duì)這兩個(gè)64位變量的寫操作不具有原子性。4. volatile的內(nèi)存語義
4.1 volatile的特性
理解volatile特性的一個(gè)好方法是對(duì)volatile變量的單個(gè)讀、寫,看成是使用同一個(gè)鎖對(duì)這些單個(gè)讀、寫操作做了同步。
簡而言之,volatile變量自身具有如下特性:
- 可見性:對(duì)一個(gè)volatile變量的讀,總是能看到(任意線程)對(duì)這個(gè)volatile變量的最后的寫入
- 對(duì)任意單個(gè)volatile變量的讀、寫具有原子性。但是對(duì)于類似于volatile++的復(fù)合操作不具備原子性。
4.2 volatile寫-讀建立的happens-before關(guān)系
volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫,happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀。
如下代碼:
class VolatileExample(){int a = 0;volatile boolean flag = false;public void writer(){a = 1; // 1flag = true; // 2}public void reader(){if(flag){ // 3int i = a ; // 4 .....}} }假設(shè)線程A執(zhí)行writer()方法之后,線程B執(zhí)行reader()方法,其happens-before關(guān)系的圖形化表現(xiàn)形式如下:
4.3 volatile寫-讀的內(nèi)存語義
volatile的內(nèi)存語義的總結(jié):
4.4 volatile 內(nèi)存語義的實(shí)現(xiàn)
volatile 內(nèi)存語義通過使用store、write和read、load原子操作指令以及內(nèi)存屏障來實(shí)現(xiàn)。
下面是基于保守策略的JMM內(nèi)存屏障插入策略:
volatile關(guān)鍵字與鎖的同步策略的優(yōu)勢(shì)和劣勢(shì):
由于volatile僅僅保證對(duì)單個(gè)volatile變量的讀/寫具有原子性,而鎖的互斥執(zhí)行的特性可以確保對(duì)整個(gè)臨界區(qū)代碼的執(zhí)行具有原子性。在功能上,鎖比volatile更強(qiáng)大;在可伸縮性和執(zhí)行性能上,volatile更有優(yōu)勢(shì)。5. 鎖的內(nèi)存語義
5.1 鎖的釋放–獲取建立的happens-before關(guān)系
鎖除了讓臨界區(qū)互斥執(zhí)行外,還可以讓釋放鎖的線程向獲取同一個(gè)鎖的線程發(fā)送消息。
5.2 鎖的釋放后和獲取的內(nèi)存語義
總結(jié):
鎖(synchronized重量級(jí)鎖)內(nèi)存語義的實(shí)現(xiàn):
synchronized重量級(jí)鎖主要是通過lock(鎖定)、unlock(解鎖)原子指令來實(shí)現(xiàn)的。lock(鎖定)、unlock(解鎖)有兩個(gè)規(guī)則:- 如果一個(gè)變量事先沒有被lock操作鎖定,那就不允許對(duì)它執(zhí)行unlock操作,也不允許unlock一個(gè)被其他線程鎖定的變量。
- 對(duì)一個(gè)變量執(zhí)行unlock操作,必須把此變量同步到主內(nèi)存中(執(zhí)行store和write操作)
5.3 鎖(ReentrantLock)內(nèi)存語義的實(shí)現(xiàn)
下面我們來看以下代碼:
class ReentrantLockExample{int a = 0;ReentrantLock lock = new ReentrantLock();public void writer(){lock.lock(); //獲取鎖try {a++;} finally {lock.unlock(); //釋放鎖}} public void reader(){lock.lock(); //獲取鎖try {int i = a;.....} finally {lock.unlock(); //釋放鎖}} }ReentrantLock的實(shí)現(xiàn)依賴于Java同步器框架AbstractQueuedSynchronizer(AQS)。AQS使用一個(gè)整型的volatile變量來維護(hù)同步狀態(tài)。
ReentrantLock分為公平鎖和非公平鎖。ReentrantLock默認(rèn)是非公平鎖
5.3.1 非公平鎖
使用非公平鎖時(shí),加鎖方法lock()調(diào)用軌跡如下。 1)ReentrantLock:lock()。 2)NonfairSync:lock()。 3)AbstractQueuedSynchronizer:compareAndSetState(int expect,int update)。 在第3步真正開始加鎖,下面是該方法的源代碼。
protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }該方法以原子操作的方式更新state變量,本文把Java的compareAndSet()方法調(diào)用簡稱為 CAS。JDK文檔對(duì)該方法的說明如下:如果當(dāng)前狀態(tài)值等于預(yù)期值,則以原子方式將同步狀態(tài) 設(shè)置為給定的更新值。此操作具有volatile讀和寫的內(nèi)存語義。
5.3.2 公平鎖
使用公平鎖時(shí),加鎖方法lock()調(diào)用軌跡如下。
- 1)ReentrantLock:lock()。
- 2)FairSync:lock()。
- 3)AbstractQueuedSynchronizer:acquire(int arg)。
- 4)ReentrantLock:tryAcquire(int acquires)。
在第4步真正開始加鎖,下面是該方法的源代碼。
/*** Fair version of tryAcquire. Don't grant access unless* recursive call or no waiters or is first.*/protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}從上面源代碼中我們可以看出,加鎖方法首先讀volatile變量state。
在使用公平鎖時(shí),解鎖方法unlock()調(diào)用軌跡如下。
- 1)ReentrantLock:unlock()。
- 2)AbstractQueuedSynchronizer:release(int arg)。
- 3)Sync:tryRelease(int releases)。
在第3步真正開始釋放鎖,下面是該方法的源代碼。
protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;}從上面的源代碼可以看出,在釋放鎖的最后寫volatile變量state。
公平鎖在釋放鎖的最后寫volatile變量state,在獲取鎖時(shí)首先讀這個(gè)volatile變量。根據(jù) volatile的happens-before規(guī)則,釋放鎖的線程在寫volatile變量之前可見的共享變量,在獲取鎖的線程讀取同一個(gè)volatile變量后將立即變得對(duì)獲取鎖的線程可見。
現(xiàn)在對(duì)公平鎖和非公平鎖的內(nèi)存語義做個(gè)總結(jié):
5.4 concurrent包的實(shí)現(xiàn)
Java的CAS會(huì)使用現(xiàn)代處理器上提供的高效機(jī)器級(jí)別的原子指令,這些原子指令以原子 方式對(duì)內(nèi)存執(zhí)行讀-改-寫操作,這是在多處理器中實(shí)現(xiàn)同步的關(guān)鍵(從本質(zhì)上來說,能夠支持原子性讀-改-寫指令的計(jì)算機(jī),是順序計(jì)算圖靈機(jī)的異步等價(jià)機(jī)器,因此任何現(xiàn)代的多處理器都會(huì)去支持某種能對(duì)內(nèi)存執(zhí)行原子性讀-改-寫操作的原子指令)。
如果我們仔細(xì)分析concurrent包的源代碼實(shí)現(xiàn),會(huì)發(fā)現(xiàn)一個(gè)通用化的實(shí)現(xiàn)模式。
AQS,非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類(java.util.concurrent.atomic包中的類),這些concurrent包中的基礎(chǔ)類都是使用這種模式來實(shí)現(xiàn)的,而concurrent包中的高層類又是依賴于這些基礎(chǔ)類來實(shí)現(xiàn)的。
6. final域的內(nèi)存語義
6.1 final域的重排序規(guī)則
示例代碼如下:
public clas FinalExample{int i ; // 普通變量final int j; // final變量static FinalExample obj;public FinalExample(){ // 構(gòu)造函數(shù)i = 1; // 寫普通域j = 2; // 寫final域}public static void writer(){ // 寫線程A執(zhí)行obj = new FinalExample();}public static void reader(){ // 讀線程B執(zhí)行FinalExample object = obj; // 讀對(duì)象引用int a = object.i; // 讀普通域int b = object.j; // 讀final域} }假設(shè)一個(gè)線程A執(zhí)行writer()方法,隨后另一個(gè)線程B執(zhí)行reader()方法。
6.2 寫final域的重排序規(guī)則
6.3 讀final域的重排序規(guī)則
在一個(gè)線程中,初次讀對(duì)象引用與初次讀該對(duì)象包含的final域,JMM禁止處理器重排序這兩個(gè)操作。編譯器會(huì)在讀final域操作的前面插入一個(gè)LoadLoad屏障。
6.4 final域?yàn)橐妙愋?/h2>
在構(gòu)造函數(shù)內(nèi)對(duì)一個(gè)final引用的對(duì)象的成員域的寫入,與隨后在構(gòu)造函數(shù)外把這個(gè)被構(gòu)造對(duì)象的引用賦值給一個(gè)引用變量,這兩個(gè)操作之間不能重排序。
讀者可能會(huì)問:為什么final引用不能從構(gòu)造函數(shù)內(nèi)“溢出”?
在構(gòu)造函數(shù)返回前,被構(gòu)造對(duì)象的引用不能為其他線程所見,因?yàn)榇藭r(shí)final域可能還沒有被初始化。在構(gòu)造函數(shù)返回后,任意線程都將保證能看到final域正確初始化之后的值。7. happens-before 先行發(fā)生原則
7.1 JMM的設(shè)計(jì)
設(shè)計(jì)意圖:
JMM對(duì)這兩種不同性質(zhì)的重排序,采取了不同的策略,如下。
7.2 happens-before定義
JSR-133使用happens-before的概念來指定兩個(gè)操作之間的執(zhí)行順序。先行發(fā)生是Java內(nèi)存模型中定義的兩項(xiàng)操作之間的偏序關(guān)系。如果說操作A先行發(fā)生于操作B,其實(shí)就是說在發(fā)生操作B之前,操作A產(chǎn)生的影響能被操作B觀察到。
7.3 happens-before原則
- 1)程序順序規(guī)則:一個(gè)線程中的每個(gè)操作,happens-before于該線程中的任意后續(xù)操作。
- 2)監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖,happens-before于隨后對(duì)這個(gè)鎖的加鎖。
- 3)volatile變量規(guī)則:對(duì)一個(gè)volatile域的寫,happens-before于任意后續(xù)對(duì)這個(gè)volatile域的讀。
- 4)傳遞性:如果A happens-before B,且B happens-before C,那么A happens-before C。
- 5)start()規(guī)則:如果線程A執(zhí)行操作ThreadB.start()(啟動(dòng)線程B),那么A線程的 ThreadB.start()操作happens-before于線程B中的任意操作。
- 6)join()規(guī)則:如果線程A執(zhí)行操作ThreadB.join()并成功返回,那么線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功返回。
7.4 雙重檢查鎖定與延遲初始化
雙重檢查鎖定與延遲初始化在Java多線程程序中,有時(shí)候需要采用延遲初始化來降低初始化類和創(chuàng)建對(duì)象的開銷。雙重檢查鎖定是常見的延遲初始化技術(shù),但它是一個(gè)錯(cuò)誤的用法.
7.4.1雙重檢查鎖定的由來
假設(shè)在Java程序程序中,我們需要使用單例設(shè)計(jì)模式。下面是一個(gè)簡單的單例設(shè)計(jì)模式:
public class UnsafeLazyInitialization {private static Instance instance;public static Instance getInstance() {if (instance == null) // 1:A線程執(zhí)行instance = new Instance(); // 2:B線程執(zhí)行return instance;} }問題是:上面代碼描述的單例模式不是線程安全的。如果有多個(gè)線程同時(shí)訪問時(shí),訪問結(jié)果是線程不安全的。所以我們只需要添加同步鎖synchronized關(guān)鍵字即可
public class SafeLazyInitialization {private static Instance instance;public synchronized static Instance getInstance() {if (instance == null)instance = new Instance();return instance;}}問題:由于對(duì)getInstance()方法做了同步處理,synchronized將導(dǎo)致性能開銷。如果getInstance()方法被多個(gè)線程頻繁的調(diào)用,將會(huì)導(dǎo)致程序執(zhí)行性能的下降。為了解決這個(gè)問題,人們就提出了使用雙重檢查鎖定來實(shí)現(xiàn)延遲初始化的示例代碼。
public class DoubleCheckedLocking { // 1private static Instance instance; // 2public static Instance getInstance() { // 3if (instance == null) { // 4:第一次檢查synchronized (DoubleCheckedLocking.class) { // 5:加鎖if (instance == null) // 6:第二次檢查instance = new Instance(); // 7:問題的根源出在這里} // 8} // 9return instance; // 10} // 11 }如上面代碼所示,如果第一次檢查instance不為null,那么就不需要執(zhí)行下面的加鎖和初始 化操作。因此,可以大幅降低synchronized帶來的性能開銷。上面代碼表面上看起來,似乎兩全其美。但是它是錯(cuò)誤的,上面代碼在執(zhí)行的過程中,也會(huì)造成線程不安全的問題。
7.4.2 出現(xiàn)問題的原因
簡單來說,出現(xiàn)問題的元素就是指令重排序的問題。
前面的雙重檢查鎖定示例代碼的第7行(instance=new Singleton();)創(chuàng)建了一個(gè)對(duì)象。這一行代碼可以分解為如下的3行偽代碼。
memory = allocate(); // 1:分配對(duì)象的內(nèi)存空間 ctorInstance(memory); // 2:初始化對(duì)象 instance = memory; // 3:設(shè)置instance指向剛分配的內(nèi)存地址上面代碼可能會(huì)進(jìn)行重排序:
memory = allocate(); // 1:分配對(duì)象的內(nèi)存空間 instance = memory; // 3:設(shè)置instance指向剛分配的內(nèi)存地址 // 注意,此時(shí)對(duì)象還沒有被初始化! ctorInstance(memory); // 2:初始化對(duì)象在知曉了問題發(fā)生的根源之后,我們可以想出兩個(gè)辦法來實(shí)現(xiàn)線程安全的延遲初始化。
7.4.3 基于volatile的解決方案
volatile關(guān)鍵字禁止指令重排序。我們可以利用這個(gè)特性,使用volatile關(guān)鍵字來解決這個(gè)問題:
public class SafeDoubleCheckedLocking {private volatile static Instance instance;public static Instance getInstance() {if (instance == null) {synchronized (SafeDoubleCheckedLocking.class) {if (instance == null)instance = new Instance(); // instance為volatile,現(xiàn)在沒問題了}}return instance;} }7.4.4 基于類初始化的解決方案
JVM在類的初始化階段(即在Class被加載后,且被線程使用之前),會(huì)執(zhí)行類的初始化。在 執(zhí)行類的初始化期間,JVM會(huì)去獲取一個(gè)鎖。這個(gè)鎖可以同步多個(gè)線程對(duì)同一個(gè)類的初始化。
public class InstanceFactory {private static class InstanceHolder {public static Instance instance = new Instance();}public static Instance getInstance() {return InstanceHolder.instance ; // 這里將導(dǎo)致InstanceHolder類被初始化} }這個(gè)方案的實(shí)質(zhì)是:允許3.8.2節(jié)中的3行偽代碼中的2和3重排序,但不允許非構(gòu)造線程(這 里指線程B)“看到”這個(gè)重排序。
Java虛擬機(jī)類加載的條件:
- 1)T是一個(gè)類,而且一個(gè)T類型的實(shí)例被創(chuàng)建。
- 2)T是一個(gè)類,且T中聲明的一個(gè)靜態(tài)方法被調(diào)用。
- 3)T中聲明的一個(gè)靜態(tài)字段被賦值。
- 4)T中聲明的一個(gè)靜態(tài)字段被使用,而且這個(gè)字段不是一個(gè)常量字段。
- 5)T是一個(gè)頂級(jí)類(Top Level Class,見Java語言規(guī)范的§7.6),而且一個(gè)斷言語句嵌套在T內(nèi)部被執(zhí)行。
Java語言規(guī)范規(guī)定,對(duì)于每一個(gè)類或接口C,都有一個(gè)唯一的初始化鎖LC與之對(duì)應(yīng)。從C 到LC的映射,由JVM的具體實(shí)現(xiàn)去自由實(shí)現(xiàn)。JVM在類初始化期間會(huì)獲取這個(gè)初始化鎖,并且 每個(gè)線程至少獲取一次鎖來確保這個(gè)類已經(jīng)被初始化過了.
7.4.5 多線程類加載處理過程
第1階段
第一階段:通過在Class對(duì)象上同步(即獲取Class對(duì)象的初始化鎖),來控制類或接口的初始化。這個(gè)獲取鎖的線程會(huì)一直等待,直到當(dāng)前線程能夠獲取到這個(gè)初始化鎖。
第二階段
第2階段:線程A執(zhí)行類的初始化,同時(shí)線程B在初始化鎖對(duì)應(yīng)的condition上等待。
第三階段
第3階段:線程A設(shè)置state=initialized,然后喚醒在condition中等待的所有線程。
第四階段
第4階段:線程B結(jié)束類的初始化處理。
第五階段
第5階段:線程C執(zhí)行類的初始化的處理。
8.Java內(nèi)存模型綜述
8.1 處理器的內(nèi)存模型
處理器的內(nèi)存模型順序一致性內(nèi)存模型是一個(gè)理論參考模型,JMM和處理器內(nèi)存模型在設(shè)計(jì)時(shí)通常會(huì)以順序一致性內(nèi)存模型為參照。
9.2 各種內(nèi)存模型之間的關(guān)系
JMM是一個(gè)語言級(jí)的內(nèi)存模型,處理器內(nèi)存模型是硬件級(jí)的內(nèi)存模型,順序一致性內(nèi)存 模型是一個(gè)理論參考模型。
9.3 JMM的內(nèi)存可見性保證
按程序類型,Java程序的內(nèi)存可見性保證可以分為下列3類。
總結(jié)
以上是生活随笔為你收集整理的blp模型 上读下写_Java高并发编程(三):Java内存模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: expdp oracle 并行_关于Ex
- 下一篇: java 与c 运行效率_Java语言与