1. 內存模型概念多線程
(1)內存模型(Java Memory Model)和內存結構(堆棧那些)不是一個層面的概念,JMM 定義了一套在多線程讀寫共享數據(成員變量,靜態變量等,而不是局部變量這種線程私有的)時,對數據的可見性、有序性、和原子性的規則和保障。併發
(2)JMM規定了全部的變量都存儲在主內存(虛擬機內存的一部分,但能夠和操做系統的類比)中;ide
每條線程還有本身的工做內存,線程的工做內存中保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的全部操做(讀取,賦值)都必須在工做內存中進行,而不能直接讀寫主內存中的變量;優化
不一樣的線程之間也沒法直接訪問對方的工做內存中的變量,線程間變量的值傳遞均須要經過主內存來完成。操作系統
(3)若是非要把主內存工做內存,和內存結構的堆棧方法區對應,那麼主內存應該對應堆中的對象的實例數據部分,而工做內存對應棧。線程
2. 原子性對象
要執行就執行完,不能執行一半blog
違背原子性例子:兩個線程對一個靜態變量 i=0 執行 i++ 和 i--(每一個對應四條JVM字節碼指令),可能致使結果不是0內存
解決:用synchronized加鎖,加鎖位置應儘可能減小代碼獲取釋放鎖的次數編譯器
synchronized( 對象 ) { 要做爲原子操做代碼 }
3. 可見性
指一個對象能看到或訪問另外一個對象的能力
違背可見性的例子:t線程看不到主線程run變量的改變
public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{while(run){// .... } }); t.start(); Thread.sleep(1000); run = false; // 線程t不會如預想的停下來}
解決:volatile(易變關鍵字),它能夠用來修飾成員變量和靜態成員變量,線程操做 volatile 變量都是直接操做主存
所以上面的run被修改後會存到主內存,t線程訪問主內存內容會看到改變
4. 有序性
代碼按順序執行
違背有序性的例子:因爲即時編譯器在運行時會有指令重排的優化,多線程狀況下可能出現非預期結果
解決:volatile 修飾的變量,能夠禁用指令重排
所以volatile能夠保證可見性和有序性,不能保證原子性,但屬於輕量級併發控制;而synchronized能夠保證三者,但更重量級
5. CAS
CAS 即 Compare and Swap ,它的實現用的是樂觀鎖的思想
// 須要不斷嘗試while(true) {int 舊值 = 共享變量 ; // 好比拿到了當前值 0int 結果 = 舊值 + 1; // 在舊值 0 的基礎上增長 1 ,正確結果是 1if( compareAndSwap ( 舊值, 結果 )) {// 成功,退出循環 } }