JSR:https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html
很早的譯文:http://snake1987.iteye.com/blog/983254
What is a memory model, anyway?
- 在處理器的層次,一個(gè)存儲(chǔ)模型定義必要的并且足夠的條件,讓當(dāng)前的處理器可以看到另外的處理器寫進(jìn)一個(gè)儲(chǔ)存(memory,如一個(gè)變量),并且當(dāng)前的處理器的寫進(jìn)memory能被別的處理器看到。一些處理器展示了一個(gè)強(qiáng)壯(Strong)的存儲(chǔ)模型,他可以讓所有的處理器看到儲(chǔ)存在一個(gè)memory的正確的相同的值。其他的處理器展示了一個(gè)較弱的存儲(chǔ)模型,它提供一些特殊的指令,叫做存儲(chǔ)屏障,這些指令可以flush或者invalidate這個(gè)本地寄存器的高速緩存,以便可以看到別的寄存器的寫memory操作和讓本寄存器的寫memory被別的寄存器看到。當(dāng)執(zhí)行l(wèi)ock或者unlock動(dòng)作的時(shí)候,這些存儲(chǔ)屏障通常會(huì)表現(xiàn)出來(lái),他們?cè)谝粋€(gè)高層語(yǔ)言是對(duì)編程人員是不可見的
- 有時(shí),用強(qiáng)?。⊿trong)的存儲(chǔ)模型寫程序更加容易,因?yàn)榭梢詼p少存儲(chǔ)屏障的試用。然而,即使是一些最強(qiáng)健的存儲(chǔ)模型,存儲(chǔ)屏障經(jīng)常也是有必要的:quite frequently their placement is counterintuitive?,F(xiàn)代的趨勢(shì)是,處理器的設(shè)計(jì)上,較弱的內(nèi)存模型更被支持,因?yàn)樗麄兙徍土烁咚倬彺娴囊恢滦詥?wèn)題,允許了更大的吞吐率通過(guò)多處理器和大容量?jī)?nèi)存。
Do other languages, like C++, have a memory model?
- 大部分其他的程序語(yǔ)言,像c和c++,都不是設(shè)計(jì)為直接支持多線程的。這些語(yǔ)言提供的保護(hù)抵抗(against)一些種類的reorderings:像發(fā)生在編譯器的和結(jié)構(gòu)的reorder,是嚴(yán)重依賴由threading libraries(像pthreads)的使用提供的保障(沒(méi)翻譯好~),和編譯器使用,和平臺(tái)代碼運(yùn)行
What is JSR 133 about?
- 自從1997年以來(lái),一些關(guān)于jmm的嚴(yán)重的缺陷(像定義在Java Language Specification chapter 17的)已經(jīng)被發(fā)現(xiàn)。這些缺陷允許一些迷惑的舉止(像final fields被觀察到改變了他們的值)和破壞編譯器表現(xiàn)一些通常的優(yōu)化措施的能力
- Jmm是一個(gè)有野心的承諾(ambitious undertaking)。這是第一次一個(gè)程序語(yǔ)言定義嘗試去包含一個(gè)存儲(chǔ)模型,這個(gè)存儲(chǔ)模型可以為穿過(guò)一系列結(jié)構(gòu)的并發(fā)提供一致的語(yǔ)意。很不幸,定義一個(gè)一致的和直覺(jué)的(intuitive)存儲(chǔ)模型被證明是比想象中還要困難。JSR133為java語(yǔ)言定義了一個(gè)新的存儲(chǔ)模型,修復(fù)了早期存儲(chǔ)模型的缺陷。為了做到這點(diǎn),final和volatile的語(yǔ)義將被改變。
JSR133的目標(biāo)包括:
- 保留已存在的安全保證,像type-safety,并且強(qiáng)化其他的。舉個(gè)例子,變量的值不是憑空創(chuàng)造的:線程觀察到的每個(gè)變量的值必須是某個(gè)線程有原因地設(shè)置的
- 關(guān)于synchronized程序的的正確的語(yǔ)義應(yīng)該盡可能地簡(jiǎn)單和intuitive
- 不完整,不正確的synchronized程序的語(yǔ)義應(yīng)該被定義,這樣能夠讓潛在的安全隱患最小化
- 編程人員對(duì)于多線程程序與內(nèi)存的交互應(yīng)該要有有原因的信心。(也就是說(shuō)不能盲目的自信這個(gè)代碼是正確的)
- 正確地設(shè)計(jì)高性能的jvm實(shí)現(xiàn),并且能跨越大范圍的流行硬件結(jié)構(gòu)是有可能的
- 一個(gè)新的保障initialization safety應(yīng)該被提供。如果一個(gè)對(duì)象被合適的constructed(這就意味著該對(duì)象的引用在構(gòu)造(construction)期間沒(méi)有逃逸escape),這樣所有看到該對(duì)象的引用的線程也能看到他的設(shè)置在構(gòu)造函數(shù)中的final域的值,而不需要synchronization。
A new guarantee of initialization safety should be provided. If an object is properly constructed (which means that references to it do not escape during construction), then all threads which see a reference to that object will also see the values for its final fields that were set in the constructor, without the need for synchronization. - 要將對(duì)已有代碼的影響最小化
What is meant by reordering?
- 有很多的例子:訪問(wèn)一個(gè)程序變量(object instance fields,class static fields,and array elements)時(shí)可能碰到被以不同的順序執(zhí)行,而不是被定義在程序中的順序。編譯器是允許以優(yōu)化的名義來(lái)改變指令的順序的。處理器可能在一個(gè)確切的環(huán)境中執(zhí)行改變了順序的指令。數(shù)據(jù)可能在寄存器,處理器告訴緩存,和主存之間以不同的順序移動(dòng),而不是定義在程序中的順序。
- 舉個(gè)例子,如果一個(gè)線程寫一個(gè)field a,并且然后寫一個(gè)field b,并且b的值不依賴于a的值,這樣編譯器是允許去重新排序這些操作的,并且告訴緩存也是允許去flush b的值在a之前到主存上。有一系列潛在的重排序的原因,像編譯器,JIT,和高速緩存。
- 編譯器,runtime和硬件是提供一個(gè)協(xié)作去創(chuàng)建一個(gè)“似乎是順序的”的語(yǔ)義的幻想的,這就意味著,在一個(gè)單線程的程序,程序應(yīng)該不能觀察到重排序。然而,重排序會(huì)出現(xiàn)在不正確的synchronized多線程程序,在那里一個(gè)線程是能觀察到別的線程的影響的,并且能夠檢測(cè)到變量的訪問(wèn)對(duì)其他的線程變得可見,在一個(gè)不同的順序,而不是定義在程序中的順序。
大部分的時(shí)間,一個(gè)線程不關(guān)心其他的線程正在做什么。如果需要關(guān)心了,那就需要synchronization了
Synchronization做了什么?
Synchronization有很多方面的內(nèi)容,最被廣泛知道的一個(gè)是互斥:一次只有一個(gè)線程可以持有監(jiān)視器(monitor),這樣,這就意味著一次一個(gè)線程進(jìn)入了一個(gè)由一個(gè)monitor監(jiān)控的synchronized塊,其他的線程不能進(jìn)入這個(gè)代碼塊中,直到第一個(gè)線程退出
但是除了互斥之外,synchronization還有更多的內(nèi)容。Synchronization保證了一個(gè)線程的發(fā)生在同步塊之中或者之前的memory寫,可以被其他synchronize在同樣的monitor的線程看到。在我們退出了一個(gè)synchronized塊,我們釋放monitor,這樣會(huì)flushing高速緩存到主存,這樣,這個(gè)線程的寫memory對(duì)其他線程而言就是可見的。我們能夠進(jìn)入同步塊之前,我們需要獲得monitor,這個(gè)操作會(huì)讓本地處理器的高速緩存變得無(wú)效,這樣變量將會(huì)從主存中重新加載。我們?nèi)缓缶湍軌蚩吹街暗尼尫胖械乃械膍emory寫。
討論到高速緩存相關(guān)聯(lián)的,聽起來(lái)像這些事件只會(huì)影響多處理器的機(jī)器。然而,重排序的影響在但處理器機(jī)器也很容易被看見。舉個(gè)例子:編譯器移動(dòng)你的代碼在到一個(gè)acquire之前或者一個(gè)release之后是不可能的。當(dāng)我們說(shuō)acquires和releases在高速緩存起作用,我們是使用了一個(gè)數(shù)字的可能結(jié)果的速記法(這段不知道啥意思~~)
新的存儲(chǔ)模型語(yǔ)義創(chuàng)造了一個(gè)偏序的內(nèi)存操作(讀field,寫field,lock,unlock)和其他線程操作(start和join)。在那里我們說(shuō)一些動(dòng)作happen-before其他的操作。當(dāng)一個(gè)操作happen-before,首先保證了第二個(gè)操作對(duì)第一個(gè)操作是可見的。這些順序的規(guī)則在以下:Each action in a thread happens before every action in that thread that comes later in the program's order.
An unlock on a monitor happens before every subsequent lock on that same monitor.
A write to a volatile field happens before every subsequent read of that same volatile.
A call to start() on a thread happens before any actions in the started thread.
All actions in a thread happen before any other thread successfully returns from a join() on that thread.
這意味著一個(gè)線程的任何存儲(chǔ)操作對(duì)于任何線程,在他進(jìn)入一個(gè)被同一個(gè)monitor保護(hù)同步塊之后并且在退出同步塊之前是可見的,因?yàn)樗械拇鎯?chǔ)操作happen before這個(gè)release,并且release happen before這個(gè)acquire。
另一個(gè)暗示是:下面的代碼是不可用的
synchronized (new Object()) {}
This is actually a no-op,并且你的編譯器可以將他完全一出,因?yàn)榫幾g器知道不可能有其他的線程會(huì)synchronize在這個(gè)monitor。你不得不為一個(gè)想要看到另一個(gè)線程結(jié)果的線程建立一個(gè)happens before關(guān)系
注意:如果需要一個(gè)合適的happen-before關(guān)系,所有的線程synchronize在同一個(gè)monitor是很重要的。