一、Java內存模型
Java Memory Modle,簡稱 JMM,中文名稱 Java內存模型,它是一個抽象的概念,用來描述或者規范訪問內存變量的方式。因為各中計算機的操作系統和硬件不同,方式機制也可能不同,Java內存模型用于屏蔽(適配)各種差異,以此來達到訪問各個平臺的一致的效果。這也是Java夸平臺的重要原因之一。
主內存: Java內存規定了所有變量都存儲在主內存(Main Memory)中,各個線程又有自己的本地內存(工作內存),本地內存保存著主內存中部分變量。具體訪問方式如下:
JMM工作方式
**lock加鎖:**為了保證訪問主內存變量的線程安全性,在訪問前一般會加鎖處理;
**read讀:**從主內存中讀取一個變量到工作內存;
**load加載:**把read讀到的變量加載到工作內存的變量副本中;
**use使用:**此時線程可以使用其工作內存中的變量了;
**assign賦值:**將處理后的變量賦值給工作內存中的變量;
**store存儲:**將工作內存中的變量存儲到主內存中,以新建new 一個新變量的方式存儲;
**write寫:**將store存在的新變量的引用賦值給被處理的變量;
**unload解鎖:**所有的工作做完,最后解鎖釋放資源。
二、Java內存模型的三大特性
1. 原子性(Atomicity)
這里的原子性如同數據庫事務中是原子性,一個或多個操作要么全執行成功要么全執行失敗(全不執行)。
int a = 1;
a++;
double b = 1.5;
復制代碼
Java內存模型只保證單一的操作具有原子性,比如上面的 int a = 1; 是一個單子的操作,所以具有原子性。而 a++ 操作在底層會分為三個操作:1)、讀取a的值給臨時變量;2)、臨時變量a的值加1操作;3)、將加操作后的值賦值給a。每個操作都是原子的,但Java內存模型在多線程下并不能保證多操作具有整體原子性,因為它也不知道這個整體內有多少操作,用戶想要達到多操作具有整體原子性,需要對響應的代碼塊做同步(synchronous)處理,比如使用 有鎖的synchronized 或 無鎖的CAS。
2. 可見性(Visibility)
這里的可見性是內存可見性。
如上圖,線程1和線程2在未同步的情況下對共享內存(主內存)中的變量進行訪問,比如兩個線程的操作都是對變量a進行加1操作。假設線程1首先獲取主內存中變量a的值,隨后線程2又獲取了主內存變量a的值,此時它們工作內存中a的值都是1,它們各自將a的值加1操作,然后assign至工作內存,工作內存中變量a的值都是2,然后兩個線程又將值刷新到主內存,最后的結果是主內存中變量a的值是2。雖然整體對a的值加1操作做了兩次操作,但由于線程間的操作是互相隔離的,默認情況下無法感知內存變量的值在隨后的變化,也就無法訪問內存中最新的變量值,這就是內存可行性的問題。
如何解決內存可見性的問題?
對進入臨界區的線程做同步處理(比如 synchronized),同一時刻僅有一個線程能夠訪問臨界區的資源;
使用 volatile 關鍵字保證內存可見性,它能保證訪問臨界區資源的所有線程總能看到共享資源的最新值;
CAS無鎖化。
3. 有序性(Ordering)
線程內的所有操作都是有序的,既程序執行的順序按照代碼的先后順序執行。比如下面的示例:
int a = 1;
int b = 2;
int c = a + b;
復制代碼
線程內程序會先執行 int a = 1; ,然后執行 int b = 2; 最后執行int c = a + b;。