Java原子類中CAS的底層實現 從Java到c++到彙編, 深入講解cas的底層原理. 介紹原理前, 先來一個Demo 以AtomicBoolean類為例.先來一個調用cas的demo. 主線程在for語句里cas忙迴圈, 直到cas操作成功返回true為止. 而新開的一個縣城new Thread ...
Java原子類中CAS的底層實現
從Java到c++到彙編, 深入講解cas的底層原理.
介紹原理前, 先來一個Demo
以AtomicBoolean類為例.先來一個調用cas的demo.
主線程在for語句里cas忙迴圈, 直到cas操作成功返回true為止.
而新開的一個縣城new Thread 會在4秒後,將flag設置為true, 為了讓主線程能夠設置成功.(因為cas的預期值是true, 而flag被初始化為了false)
現象就是主線程一直在跑for迴圈. 4秒後, 主線程將會設置成功, 然後輸出時間差, 然後終止for迴圈.
public class TestAtomicBoolean { public static void main(String[] args) { AtomicBoolean flag = new AtomicBoolean(false); long start = System.currentTimeMillis(); new Thread(()->{ try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } flag.set(true); }).start(); for(;;){ if(flag.compareAndSet(true,false)){ System.out.println(System.currentTimeMillis() - start); System.out.println("inner loop OK!"); break; } } } }
這裡只是舉了一個例子, 也許這個例子也不太恰當, 本文只是列出了這個api的調用方法而已, 重點在於介紹compareAndSet()方法的底層原理.
Java級源碼AtomicBoolean.java
發現AtomicBoolean的compareAndSet()調用的是unsafe里的compareAndSwapInt()方法.
Java級源碼Unsafe.java
有的同學可能好奇, 其中的unsafe是怎麼來的.
在AtomicBoolean類中的靜態成員變數:
如果還要細究Unsafe.getUnsafe()是怎麼實現的話....那麼我再貼一份Unsafe類里的getUnsafe的代碼:
首先, 在Unsafe類里, 自己就有了一個自己的實例.(單例)
然後Unsafe類里的getUnsafe()方法會進行檢查, 最終會return這個單例 theUnsafe.
剛剛跑去取介紹了getUnsafe()方法...接下來繼續講解cas...
剛纔說到了AtomicBoolean類里的compareAndSet()方法內部其實調用了Unsafe類里的compareAndSwapInt()方法.
Unsafe類里的compareAndSwapInt源碼如下:
(OpenJDK8的源碼里路徑: openjdk/jdk/src/share/classes/sun/misc/Unsafe.java)
發現這裡是一段native方法.說明繼續看源碼的話, 從這裡就開始脫離Java語言了....
c++級源碼Unsafe.cpp
本源碼在OpenJDK8里的路徑為: openjdk/hotspot/src/share/vm/prims/unsafe.cpp
(這裡臨時跑題一下: 如果說要細究 UNSAFE_ENTRY 是什麼的話...UNSAFE_ENTRY 就是 JVM_ENTRY, 而 JVM_ENTRY 在interfaceSupport.hpp裡面定義了, jni相關.如果想看的話, 源碼路徑在OpenJDK8中的路徑是這個:
openjdk/hotspot/src/share/vm/runtime/interfaceSupport.hpp)
回到本文的主題cas....上面截圖的這段代碼, 看後面那一句return, 發現其中的使用到的是Atomic下的cmpxchg()方法.
c++級源碼atomic.cpp
本段源碼對應OpenJDK8的路徑是這個: openjdk/hotspot/src/share/vm/runtime/atomic.cpp
其中的cmpxchg為核心內容. 但是這句代碼根據操作系統和處理器的不同, 使用不同的底層代碼.
而atomic.inline.hpp里聲明如下:
可見 ...不同不同操作系統, 不同的處理器, 都要走不同的cmpxchg()方法的實現.
咱們接下來以其中的linux操作系統 x86處理器為例 , atomic_linux_x86.inline.hpp
彙編級源碼atomic_linux_x86.inline.hpp
OpenJDK中路徑如下: openjdk/hotspot/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
看到了__asm__, 說明c++要開始內聯彙編了,說明繼續看代碼的話, 將會是彙編語言.
這是一段內聯彙編:
其中 __asm__ volatile 指示了編譯器不要改動優化後面的彙編語句, 如果進行了優化(優化是為了減少訪問記憶體, 直接通過緩存, 加快取讀速度), 那麼就在這段函數的周期內, 某幾個變數就相當於常亮了, 其值可能會與記憶體中真實的值有差異.
未完待續....困了睡.....