日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java內存模型(JMM)及happens-before原理

瀏覽:202日期:2022-09-02 13:22:02

我們知道java程序是運行在JVM中的,而JVM就是構建在內存上的虛擬機,那么內存模型JMM是做什么用的呢?

我們考慮一個簡單的賦值問題:

int a=100;

JMM考慮的就是什么情況下讀取變量a的線程可以看到值為100。看起來這是一個很簡單的問題,賦值之后不就可以讀到值了嗎?

但是上面的只是我們源碼的編寫順序,當把源碼編譯之后,在編譯器中生成的指令的順序跟源碼的順序并不是完全一致的。處理器可能采用亂序或者并行的方式來執行指令(在JVM中只要程序的最終執行結果和在嚴格串行環境中執行結果一致,這種重排序是允許的)。并且處理器還有本地緩存,當將結果存儲在本地緩存中,其他線程是無法看到結果的。除此之外緩存提交到主內存的順序也肯能會變化。

上面提到的種種可能都會導致在多線程環境中產生不同的結果。在多線程環境中,大部分時間多線程都是在執行各自的任務,只有在多個線程需要共享數據的時候,才需要協調線程之間的操作。

而JMM就是JVM中必須遵守的一組最小保證,它規定了對于變量的寫入操作在什么時候對其他線程是可見的。

重排序

上面講了JVM中的重排序,這里我們舉個例子,以便大家對重排序有一個更深入的理解:

@Slf4jpublic class Reorder { int x=0, y=0; int a=0, b=0; private void reorderMethod() throws InterruptedException { Thread one = new Thread(()->{ a=1; x=b; }); Thread two = new Thread(()->{ b=1; y=a; }); one.start(); two.start(); one.join(); two.join(); log.info('{},{}', x, y); } public static void main(String[] args) throws InterruptedException { for (int i=0; i< 100; i++){ new Reorder().reorderMethod(); } }}

上面的例子是一個很簡單的并發程序。由于我們沒有使用同步限制,所以線程one和two的執行順序是不定的。有可能one在two之前執行,也有可能在two之后執行,也可能兩者同時執行。不同的執行順序可能會導致不同的輸出結果。

同時雖然我們在代碼中指定了先執行a=1, 再執行x=b,但是這兩條語句實際上是沒有關系的,在JVM中完全可能將兩條語句重排序成x=b在前,a=1在后,從而導致輸出更多意想不到的結果。

Happens-Before

為了保證java內存模型中的操作順序,JMM為程序中的所有操作定義了一個順序關系,這個順序叫做Happens-Before。要想保證操作B看到操作A的結果,不管A和B是在同一線程還是不同線程,那么A和B必須滿足Happens-Before的關系。如果兩個操作不滿足happens-before的關系,那么JVM可以對他們任意重排序。

我們看一下happens-before的規則:

1.程序順序規則: 如果在程序中操作A在操作B之前,那么在同一個線程中操作A將會在操作B之前執行。

注意,這里的操作A在操作B之前執行是指在單線程環境中,雖然虛擬機會對相應的指令進行重排序,但是最終的執行結果跟按照代碼順序執行是一樣的。虛擬機只會對不存在依賴的代碼進行重排序。

2.監視器鎖規則: 監視器上的解鎖操作必須在同一個監視器上面的加鎖操作之前執行。鎖我們大家都很清楚了,這里的順序必須指的是同一個鎖,如果是在不同的鎖上面,那么其執行順序也不能得到保證。

3.volatile變量規則: 對volatile變量的寫入操作必須在對該變量的讀操作之前執行。原子變量和volatile變量在讀寫操作上面有著相同的語義。

4.線程啟動規則: 線程上對Thread.start的操作必須要在該線程中執行任何操作之前執行。

5.線程結束規則: 線程中的任何操作都必須在其他線程檢測到該線程結束之前執行。

6.中斷規則: 當一個線程再另一個線程上調用interrupt時,必須在被中斷線程檢測到interrupt調用之前執行。

7.終結器規則: 對象的構造函數必須在啟動該對象的終結器之前執行完畢。

8.傳遞性: 如果操作A在操作B之前執行,并且操作B在操作C之前執行,那么操作A必須在操作C之前執行。

上面的規則2很好理解,在加鎖的過程中,不允許其他的線程獲得該鎖,也意味著其他的線程必須等待鎖釋放之后才能加鎖和執行其業務邏輯。

4,5,6,7規則也很好理解,只有開始,才能結束。這符合我們對程序的一般認識。

8的傳遞性相信學過數學的人應該也不難理解。

接下來我們重點討論一下規則3和規則1的結合。討論之前我們再總結一下happens-before到底是做什么的。

因為JVM會對接收到的指令進行重排序,為了保證指令的執行順序,我們才有了happens-before規則。上面講到的2,3,4,5,6,7規則可以看做是重排序的節點,這些節點是不允許重排序的,只有在這些節點之間的指令才允許重排序。

結合規則1程序順序規則,我們得到其真正的含義:代碼中寫在重排序節點之前的指令,一定會在重排序節點執行之前執行。

重排序節點就是一個分界點,它的位置是不能夠移動的。看一下下面的直觀例子:

Java內存模型(JMM)及happens-before原理

線程1中有兩個指令:set i=1, set volatile a=2。

線程2中也有兩個指令:get volatile a, get i。

按照上面的理論,set和get volatile是兩個重排序節點,set必須排在get之前。而依據規則1,代碼中set i=1 在set volatile a=2之前,因為set volatile是重排序節點,所以需要遵守程序順序執行規則,從而set i=1要在set volatile a=2之前執行。同樣的道理get volatile a在get i之前執行。最后導致i=1在get i之前執行。

這個操作叫做借助同步。

安全發布

我們經常會用到單例模式來創建一個單的對象,我們看下下面的方法有什么不妥:

public class Book { private static Book book; public static Book getBook(){ if(book==null){ book = new Book(); } return book; }}

上面的類中定義了一個getBook方法來返回一個新的book對象,返回對象之前,我們先判斷了book是否為空,如果不為空的話就new一個book對象。

初看起來,好像沒什么問題,但是如果仔細考慮JMM的重排規則,就會發現問題所在。book=new Book()其實一個復雜的命令,并不是原子性操作。它大概可以分解為1.分配內存,2.實例化對象,3.將對象和內存地址建立關聯。

其中2和3有可能會被重排序,然后就有可能出現book返回了,但是還沒有初始化完畢的情況。從而出現不可以預見的錯誤。

根據上面我們講到的happens-before規則, 最簡單的辦法就是給方法前面加上synchronized關鍵字:

public class Book { private static Book book; public synchronized static Book getBook(){ if(book==null){ book = new Book(); } return book; }}

我們再看下面一種靜態域的實現:

public class BookStatic { private static BookStatic bookStatic= new BookStatic(); public static BookStatic getBookStatic(){ return bookStatic; }}

JVM在類被加載之后和被線程使用之前,會進行靜態初始化,而在這個初始化階段將會獲得一個鎖,從而保證在靜態初始化階段內存寫入操作將對所有的線程可見。

上面的例子定義了static變量,在靜態初始化階段將會被實例化。這種方式叫做提前初始化。

下面我們再看一個延遲初始化占位類的模式:

public class BookStaticLazy { private static class BookStaticHolder{ private static BookStaticLazy bookStatic= new BookStaticLazy(); } public static BookStaticLazy getBookStatic(){ return BookStaticHolder.bookStatic; }}

上面的類中,只有在調用getBookStatic方法的時候才會去初始化類。

接下來我們再介紹一下雙重檢查加鎖。

public class BookDLC { private volatile static BookDLC bookDLC; public static BookDLC getBookDLC(){ if(bookDLC == null ){ synchronized (BookDLC.class){if(bookDLC ==null){ bookDLC=new BookDLC();} } } return bookDLC; }}

上面的類中檢測了兩次bookDLC的值,只有bookDLC為空的時候才進行加鎖操作。看起來一切都很完美,但是我們要注意一點,這里bookDLC一定要是volatile。

因為bookDLC的賦值操作和返回操作并沒有happens-before,所以可能會出現獲取到一個僅部分構造的實例。這也是為什么我們要加上volatile關鍵詞。

初始化安全性

本文的最后,我們將討論一下在構造函數中含有final域的對象初始化。

對于正確構造的對象,初始化對象保證了所有的線程都能夠正確的看到由構造函數為對象給各個final域設置的正確值,包括final域可以到達的任何變量(比如final數組中的元素,final的hashMap等)。

public class FinalSafe { private final HashMap<String,String> hashMap; public FinalSafe(){ hashMap= new HashMap<>(); hashMap.put('key1','value1'); }}

上面的例子中,我們定義了一個final對象,并且在構造函數中初始化了這個對象。那么這個final對象是將不會跟構造函數之后的其他操作重排序。

本文的例子可以參考https://github.com/ddean2009/learn-java-concurrency/tree/master/reorder

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持好吧啦網。

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲精品日本| 日韩精品电影| 青青草国产成人99久久| 精品国产一区二| 亚洲一区二区三区免费在线观看| 国产欧美91| 亚洲欧美日韩国产一区| 久久精品网址| 亚洲三级网站| 日韩中文在线播放| 国产精品s色| 国产精品婷婷| 精品三级在线观看视频| 亚洲专区在线| 日本不卡免费高清视频在线| 亚洲精品第一| 欧美精品一二| 97精品国产福利一区二区三区| 亚洲精一区二区三区| 99精品小视频| 国产成人精品一区二区免费看京 | 亚洲精品激情| 免费观看亚洲| 久久精品999| 在线日韩成人| 亚洲男女av一区二区| 精品精品99| 91亚洲精品视频在线观看| 一区在线免费| 亚洲网站视频| 成人av三级| 卡一精品卡二卡三网站乱码| 日本亚洲视频在线| 国产一区91| 999国产精品| 高清日韩中文字幕| 久久99视频| 国产视频网站一区二区三区| 亚洲欧美日韩国产一区| 欧美成人日韩| 国产麻豆久久| 精品国产免费人成网站| 免费一级欧美在线观看视频| 日韩一区二区三区四区五区| 久热精品在线| av不卡在线| 日韩视频在线一区二区三区| 韩国精品主播一区二区在线观看| 精品一区二区三区中文字幕视频 | 奇米狠狠一区二区三区| 在线精品亚洲| 久久国产66| 亚洲在线观看| 蘑菇福利视频一区播放| 狠狠爱成人网| 尹人成人综合网| 在线亚洲成人| 国产一区91| 一级欧美视频| 日韩一区二区三区免费视频 | 一区二区91| 日韩福利视频网| 欧美日韩调教| 国产精品伊人| 麻豆91精品视频| 国产一区二区三区四区五区传媒| 成人午夜网址| 久久人人精品| 久久亚洲成人| 黑丝一区二区三区| 亚洲午夜国产成人| 久久精品99久久久| 国产一区二区三区不卡视频网站 | 美女视频免费精品| 成人精品国产亚洲| 韩国三级一区| 亚洲va中文在线播放免费| 三级精品视频| 亚洲精品在线观看91| 香蕉精品999视频一区二区| 男女性色大片免费观看一区二区 | 麻豆91在线播放| 日韩免费久久| 美女网站一区| 综合激情视频| 国产毛片久久久| 精品国产日韩欧美精品国产欧美日韩一区二区三区 | 天堂成人国产精品一区| 国产无遮挡裸体免费久久| 国产精品v日韩精品v欧美精品网站| 成人污污视频| 亚洲女同中文字幕| 日韩一区中文| 欧美国产美女| 老司机精品久久| 国产精品亚洲二区| 日韩国产在线| 一区二区三区网站| 国产第一亚洲| 鲁大师影院一区二区三区| 久久国产尿小便嘘嘘| 精品成人18| 9久re热视频在线精品| 国产一精品一av一免费爽爽| 日本а中文在线天堂| 午夜在线视频一区二区区别| 国产麻豆精品| 亚洲四虎影院| 日本国产精品| 视频一区日韩精品| 国产91在线精品| 午夜久久黄色| 国产精品久av福利在线观看| 91精品亚洲| 国产午夜一区| 欧美+亚洲+精品+三区| 天海翼亚洲一区二区三区| 精品视频97| 丝袜美腿亚洲色图| 久久久久伊人| 国产亚洲在线| 红杏一区二区三区| 免费成人在线观看| 国产成人久久精品一区二区三区| 美女精品在线| 国产精品原创| 亚洲精品欧洲| 日韩在线不卡| 91av一区| 伊人久久成人| 中文在线а√天堂| 青青草91久久久久久久久| 欧美日韩在线观看视频小说| 国产欧美另类| 日韩一区二区免费看| 成人在线观看免费视频| 亚洲精品一级| 久久国产亚洲精品| 国产人成精品一区二区三| 欧美日韩国产亚洲一区| 精品久久精品| 欧美一区91| 丝瓜av网站精品一区二区| 国产一二在线播放| 欧美在线91| 亚洲欧美日韩精品一区二区| 国产精品成人a在线观看| 日韩一区二区三区免费视频| 99久久99久久精品国产片果冰 | 欧美成人aaa| 亚洲精品婷婷| 欧美福利一区| 日韩国产一区二区三区| 国产欧美日韩影院| 亚洲人成网站在线在线观看| 欧美在线观看视频一区| 国内精品伊人| 久久av日韩| 国产免费播放一区二区| 亚洲欧美网站在线观看| 99国产一区| 欧美日中文字幕| 日韩伦理在线一区| 久久精品国产成人一区二区三区| 日本视频在线一区| 蜜臀精品一区二区三区在线观看 | 日本免费新一区视频| 综合色一区二区| 在线午夜精品| 亚洲激情五月| 亚洲91视频| 激情久久久久久久| 天堂√中文最新版在线| 精品视频在线观看网站| 久久久久观看| 国产精品三级| 国产精品激情| 国产精品免费99久久久| 欧美午夜网站| 97久久中文字幕| 日本午夜精品一区二区三区电影 | 国产亚洲久久| 国产精品午夜av| 国产欧美啪啪| 国产精品久久777777毛茸茸| 国产亚洲观看| 国产伦精品一区二区三区视频| 天堂va在线高清一区| 亚洲精品少妇| 7m精品国产导航在线| 97se亚洲| 国产精品久久久久久妇女| 国产精品观看| 精品日韩一区| www.com.cn成人| 91tv亚洲精品香蕉国产一区| 99久久夜色精品国产亚洲狼| 欧美日韩国产高清电影| 日韩一级网站| 亚洲精品日本|