知識點整理2 Java記憶體模型

2021-08-29 04:02:09 字數 2511 閱讀 4235

原子性操作指相應的操作是單一不可分割的操作。例如,對int變數count執行count++d操作就不是原子性操作。因為count++實際上可以分解為3個操作:(1)讀取變數count的當前值;(2)拿count的當前值和1做加法運算;(3)將加完後的值賦給count變數。

在多執行緒環境中,非原子操作可能會受其他執行緒的干擾。比如,上述例子如果沒有對相應的**進行同步(synchronization)處理,則可能出現在執行第2個操作的時候,count變數的值已經被其他執行緒修改過了。當然,synchronized關鍵字可以幫助我們實現原子性操作,以避免這種執行緒間的干擾情況。

synchronized關鍵字可以實現操作的原子性,其實質是:通過該關鍵字所包括的臨界區(critical section)的排他性保證在任何乙個時刻只有乙個執行緒能夠執行臨界區中的**,這使得臨界區中的**代表了乙個原子操作。這一點,大家基本都很清楚。但是,synchronized關鍵字所起到的另乙個作用——保證記憶體的可見性(memory visibility),也是我們值得回顧的地方。

cpu在執行**的時候,為了減少變數訪問的時間消耗可能將**中訪問的變數的值快取到該cpu快取區中,因此,相應的**再次訪問該變數的時候,相應的值可能從cpu快取中而不是主記憶體中讀取的。同樣的,**對這些被快取過的變數的值的修改也可能僅是被寫入cpu快取區,而沒有寫入主記憶體。由於每個cpu都有自己的快取區,因此乙個cpu快取區中的內容對於其他cpu而言是不可見的。這就導致了在其他cpu上執行的其他執行緒可能無法「看到」該執行緒對某個變數值的更改。這就是所謂的記憶體可見性。

synchronized關鍵字的另乙個作用就是保證了乙個執行緒執行臨界區中的**時,所修改的變數值對於稍後執行該臨界區的執行緒來說是可見的。這對於保證多執行緒**的正確性來說非常重要。

而volatile關鍵字也能夠保證記憶體可見性。即乙個執行緒對乙個採用volatile關鍵字修飾的變數的值的更改,對於其他訪問該變數的執行緒而言總是可見的。也就是說,其他執行緒不會讀到乙個「過期」的變數值。因此,有人將volatile關鍵字和synchronized關鍵字所代表的內部鎖做比較,將其稱為輕量級的鎖。這種稱呼其實並不恰當,volatile關鍵字只能保證記憶體可見性,它並不能像synchronized關鍵字所代表的內部鎖那樣能夠保證操作的原子性。volatile關鍵字實現記憶體可見性的核心機制是:當乙個執行緒修改了乙個volatile修飾的變數的值時,該值會被寫入主記憶體(即ram)而不僅僅是當前執行緒所在的cpu的快取區,而其他cpu的快取區中儲存的該變數的值也會因此而失效(從而得以更新為主記憶體中該變數的「新值」)。這就保證了其他執行緒訪問該volatile修飾的變數時,總是可以獲取到該變數的最新值。

volatile關鍵字的另乙個作用是:它禁止了指令重排序(re-order)。編譯器和cpu為了提高指令的執行效率可能會進行指令重排序,這使得**的實際執行方式可能不是按照我們所認為的方式進行。例如下面的例項變數初始化語句:

private someclass someobject = new someclass();

上述語句非常簡單:(1)建立類someclass 的例項;(2)將類someclass 的例項的引用賦給變數someobject 。但是由於指令的重排序作用,這段**的實際執行順序可能是:(1)分配一段用於儲存someclass 例項的記憶體空間;(2)將對該記憶體空間的引用賦給變數someobject;(3)建立類someclass 的例項。因此,當其他執行緒訪問someobject變數的值時,其得到的僅是指向一段儲存someclass 例項的的記憶體空間的引用而已,而該記憶體空間相應的someclass 例項的初始化可能尚未完成,這就可能導致一些意想不到的結果。而禁止指令重排序則是可以使得上述**按照我們所期望的順序(正如**所表達的順序)來執行。

禁止指令重排序雖然導致編譯器和cpu無法對一些指令進行可能的優化,但是它某種程度上讓**執行看起來更符合我們的期望。

1.volatile本質是在告訴jvm當前變數在暫存器(工作記憶體)中的值是不確定的,需要從主存中讀取;synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住。

2.volatile僅能使用在變數級別;synchronized則可以使用在變數、方法、和類級別的。

3.volatile僅能實現變數的修改可見性,不能保證原子性(執行緒a修改了變數還沒結束時,另外的執行緒b可以看到已修改的值,而且可以修改這個變數,而不用等待a釋放鎖,因為volatile 變數沒上鎖);而synchronized則可以保證變數的修改可見性和原子性。

4.volatile不會造成執行緒的阻塞;synchronized可能會造成執行緒的阻塞和上下文切換。

5.volatile標記的變數不會被編譯器優化;synchronized標記的變數可以被編譯器優化。

6.在使用volatile關鍵字時要慎重,並不是只要簡單型別變數使用volatile修飾,對這個變數的所有操作都是原子操作。當變數的值由自身決定時,如n=n+1、n++ 等,volatile關鍵字將失效。只有當變數的值和自身無關時對該變數的操作才是原子級別的,如n = m + 1,這個就是原級別的。所以在使用volatile關鍵時一定要謹慎,如果自己沒有把握,可以使用synchronized來代替volatile。

7.「鎖是昂貴的」,謹慎使用鎖機制。

java 反射知識點整理

1.1 class類 獲取class物件的三種方式 方式一 通過object類中的getobject 方法 person p new person class c p.getclass 方式二 通過 類名.class 獲取到位元組碼檔案物件 任意資料型別都具備乙個class靜態屬性,看上去要比第一種...

CSS知識點整理 2 框模型,定位

1.框模型 box model 規定了元素處理元素框處理元素內容 外邊距 邊框 內邊距的方式。2.當邊距給定的值 可以小於4個。css定義了一些規則 處理這中情況 3.外邊距合併 當兩個垂直方向 相對於水平方向 的外邊距相遇時,它們將合成乙個外邊距。合併後的數值 合併前的較大值 4.合併發生在兩個同...

知識點整理

一 標準庫容器和演算法 1.順序容器 與前面類似 2.關聯容器 map和multimap 元素包含key 鍵 和值 value 兩部分 按照鍵對元素排序 map不允許重複元素出現,但multimap可以 set和multliset 是包含已排序物件的關聯容器 只是單純的鍵的集合 set不允許重複鍵出...