java是支持多線程的,可是其可見性,原子性,有序性是致使多線程bug的緣由,因此引入java內存模型來解決這些問題。html
java內存模型歸納來講是解決可見性和有序性的。java
1)可見性 - 緩存致使緩存
當建立線程時JVM會爲其建立本身的內存存儲本身的私有變量,可是全部的共享變量都存在於主存(共享區域)中,全部線程的操做都須要在本身的私有內存中操做,多線程
因此當線程訪問共享變量時須要先將主存中變量copy到本身的工做內存,操做完後,寫會主存。 —— 故緩存會致使可見性bug併發
2)有序性 - 編譯器優化app
當語句的執行順序調整後 不會對結果形成影響時,編譯器會進行優化,調整執行順序。 —— 故會致使有序性bugide
例如:單例的double check問題,後續展開。優化
而java內存模型從某種角度來講,提供瞭解決按需禁止緩存和編譯優化的方法 。 包括 volatile,synchronized 和 final關鍵字,以及happens-before原則。spa
volatile禁用緩存,保證執行的有序性,但不能保證原子性。至關於弱化的synchronied。線程
可見性:
一、volatile修飾的共享變量,在工做內修改後,會強制立刻刷新到主存中。
二、valatile修飾的共享變量,一旦被一個線程修改完刷新到主存,則其餘工做內存中的此變量都失效,再讀取主存中的變量。
有序性:
volatile關鍵字修飾的變量以及以前的語句不會在JVM優化期間進行重排序(重排序:是指沒有數據依賴的語句進行排序。在單線程內沒有問題,優化效率還保證告終果的一致性,可是會影響併發中程序的正確性)
public class Singleton { /** * volatile修飾Singleton實例,保證singleton初始化時保證有序性 * instance = new Singleton(); * 其實JVM內部已經轉換爲多條指令: * //1:分配對象的內存空間 * memory = allocate(); * //2:初始化對象 * ctorInstance(memory); * //3:設置instance指向剛分配的內存地址 * instance = memory; * 可是通過重排序後以下: * //1:分配對象的內存空間 * memory = allocate(); * 3:設置instance指向剛分配的內存地址,此時對象還沒被初始化 * instance = memory; * //2:初始化對象 * ctorInstance(memory); * * 假設沒有volatile修飾。 * 線程1先佔有鎖,執行new Singleton(), 在給instance = memory分配內存地址的時候, * 線程2進入判斷語句singleton==null,引用地址不爲null, * 則線程2返回一個初始化不完整的實例,系統會報錯 * --------------------- */ private static volatile Singleton singleton = null; private Singleton() { } public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) {// 2 singleton = new Singleton();// 3 } } } return singleton; } }
這涉及到關鍵的happens-before原則。happens-before是指前一個操做的結果對後續是可見的。
1)程序的順序執行
2)volatile變量禁用緩存規則,對後續該變量的查看是可見的
3)傳遞性。A對於B可見,B對於C可見則A對於C可見
4)管程可見性。synchronized是管程的實現,前一個加鎖的線程操做對後一個線程時可見的。 synchronied解鎖後會強刷主存,因此後一個線程是可見的
5)線程start();這條是關於線程啓動的。它是指主線程 A 啓動子線程 B 後,子線程 B 可以看到主線程在啓動子線程 B 前的操做.
final 關鍵字則是告訴編譯器它是不變的,可勁優化
原文出處:https://www.cnblogs.com/volare/p/12227237.html