1 CAS
比較并交換,在硬體CPU層面的指令是lock cmpxchg
,
lock的含義是通過鎖住記憶體總線或者通過CPU的緩存一緻性
機制鎖住CPU緩存。cmpxchg涉及三個參數:記憶體中已有的數、要比較的數和要更新的目标數, 含義是記憶體中已有的值和和要比較的值進行比較,如果相等則将記憶體值更新為要更新的目标數,并傳回更新後的值,否則不更新記憶體值,直接傳回記憶體值。通過lock操作給總線或者緩存加鎖在硬體層面保證了這個指令在多核并發情況下的原子性。 一句話總結下:這個指令在硬體層面實作了對記憶體字“讀改寫”操作的原子性,也就是硬體層面的記憶體字加鎖排他操作。 正是這類硬體指令的存在,才有了java虛拟機和SDK層面的原子類、Lock、monitor等的加鎖正确性的保證。道家講:道生一一生二,這個lock cmpxchg就是道,一切上層鎖的基石,最原始加鎖。
cas缺點1:ABA問題:cas的原始目的是保證要修改的資料沒有被其他線程改變,然後才更新要修改的資料,判斷資料改變的方式是比較數值,如果一個值被另外一個線程改成了B又改回了A,這時本線程會誤認為資料沒有被修改過,導緻資料同步錯誤,為了避免這個問題可以使用給資料加版本号的方式解決,比如1A 2B 3A ,1A!=3A 是以就不會出現同步錯誤。
2 JAVA的鎖本質分類: 使程序阻塞的鎖和自旋鎖(本質是擷取不到鎖的時候線程阻塞和不阻塞)
阻塞鎖就是:如果擷取不到鎖就把線程挂起睡眠,直到其他線程釋放鎖的時候喚醒線程重新參與鎖競争(加鎖依賴單次CAS操作保證原子性)。
自旋鎖就是:先通過CAS操作來擷取鎖,如果擷取不到鎖就不停的循環嘗試擷取鎖,不挂起線程。
3 JAVA原子變量相關的類
原子類要解決的問題:舉個簡單的例子 int a++ 在單線程環境下是沒有問題的,但是在多線程環境下,這個a++實際是個“讀改寫”操作,至少涉及三步,如果一個線程不是“讀改寫”一次性排他的完成,就會造成邏輯上的錯誤,即同步錯誤。是以通過循環CAS操作可以保證“讀改寫”操作的一次性排他完成,即原子性,CAS提供了一個檢測資料是否被改動的機制,通過鎖總線或者緩存一緻性方式原子排他的完成。在java代碼中,就是Atomic相關的類:
AtomicXXX類、AtomicReference類、AtomicStampedReference類,AtomicIntegerFieldUpdater
Reference相關的原子類是操作資料的對象指針,StampedReference類操作的對象指針帶有版本号,用來避免ABA問題導緻的同步錯誤。對于AtomicReference,是通過CAS比較記憶體對象引用和要比較的對象引用是不是同一個(用==判斷)位址,如果是的話,替換為要替換的新對象引用,這裡的重點是對象引用的替換,也就是替換的是整個對象,而不是對象中的某些屬性字段。
AtomicIntegerFieldUpdater是通過反射的方式原子的更新對象的某個volatile int類型的字段。
實作原理:循環CAS操作。下面是源碼的部分截圖:

4 volatile 關鍵字的作用:涉及到并發 就涉及到三個概念: 可見性、順序性、原子性,volatile關鍵字保證了變量的可見性和指令的順序性。
可見性:每個CPU核心中都有自己的緩存,每個核心緩存都緩存了主存中的變量的值。多線程的情況下,每個線程都有自己的主存的緩存資料,并且互不可見,在java加鎖同步的時候,如果鎖狀态資料在不同的線程對應的CPU核心緩存中對不同的線程不可見,就會導緻同步錯誤的發生,比如多次加鎖。volatile 關鍵字可以從硬體指令層面保證:
(1)一個線程所在的核心的對應變量的緩存改變後,立馬重新整理回主記憶體。
(2)如果其他核心的緩存中也緩存了該變量,強制其緩存中的該變量失效,讓其重新從主存中加載。
這樣就保證了變量資料在不同核心運作的線程之間可見,就會避免重複加鎖之類的情況,是以java的各種鎖類中的鎖CAS變量都同時用volatile關鍵字進行了修飾。
順序性: volatile 變量對應的底層指令是記憶體屏障,能夠保證屏障指令前的指令不會重排到屏障後,屏障後的指令不會排到屏障前,在多線程并發的情況下,指令重排會導緻同步錯誤。一個典型的例子就是單例模式雙重檢測。
5 JMM java記憶體模型
5 緩存一緻性
5 java monitor
5 java AQS LOCK
5 類加載器