Java Review - 并发编程_Unsafe
文章目錄
- Unsafe
- Unsafe 提供的幾個主要的方法
- long objectFieldOffset(Field field)
- int arrayBaseOffset(Class arrayClass)
- int arrayIndexScale(Class arrayClass)
- boolean compareAndSwapLong(Object obj, long offset, long expect, long update)
- public native long getLongvolatile(Object obj, long offset)
- void putLongvolatile(Object obj, long offset, long value)
- void putOrderedLong(Object obj, long offset, long value)
- void park(boolean isAbsolute, long time)
- void unpark(Object thread)
- long getAndSetLong(Object obj, long offset, long update)
- long getAndAddLong(Object obj, long offset, long addValue)
- 使用Unsafe類
Unsafe
JDK 的 rt.jar 包中的 Unsafe 類提供了硬件級別的原子性操作,Unsafe 類中的方法都是native 方法,它們使用 JNI 的方式訪問本地 C++ 實現庫。
Unsafe 提供的幾個主要的方法
下面我們來了解一下 Unsafe 提供的幾個主要的方法以及編程時如何使用 Unsafe 類做一些事情。
long objectFieldOffset(Field field)
返回指定的變量在所屬類中的內存偏移地址,該偏移地址僅僅在該 Unsafe 函數中訪問指定字段時使用。
如下代碼使用 Unsafe 類獲取變量 value 在 AtomicLong 對象中的內存偏移
int arrayBaseOffset(Class arrayClass)
獲取數組中第一個元素的地址
int arrayIndexScale(Class arrayClass)
獲取數組中一個元素占用的字節
boolean compareAndSwapLong(Object obj, long offset, long expect, long update)
比較對象obj中偏移量為offset的變量的值是否與expect相等,相等則使用update值更新,然后返回true,否則返回false
public native long getLongvolatile(Object obj, long offset)
獲取對象obj中偏移量為offset的變量對應volatile語義的值
void putLongvolatile(Object obj, long offset, long value)
設置obj對象中offset偏移的類型為long的field的值為value,支持volatile語義
void putOrderedLong(Object obj, long offset, long value)
設置obj對象中offset偏移地址對應的long型field的值為value。這是一個有延遲的putLongvolatile方法,并且不保證值修改對其他線程立刻可見。只有在變量使用volatile修飾并且預計會被意外修改時才使用該方法。
void park(boolean isAbsolute, long time)
阻塞當前線程,其中參數isAbsolute等于false且time等于0表示一直阻塞。time大于0表示等待指定的time后阻塞線程會被喚醒,這個time是個相對值,是個增量值,也就是相對當前時間累加time后當前線程就會被喚醒。
如果isAbsolute等于true,并且time大于0,則表示阻塞的線程到指定的時間點后會被喚醒,這里time是個絕對時間,是將某個時間點換算為ms后的值。
另外,當其他線程調用了當前阻塞線程的interrupt方法而中斷了當前線程時,當前線程也會返回,而當其他線程調用了unPark方法并且把當前線程作為參數時當前線程也會返回。
void unpark(Object thread)
喚醒調用park后阻塞的線程
下面是JDK8新增的函數,這里只列出Long類型操作。
long getAndSetLong(Object obj, long offset, long update)
獲取對象obj中偏移量為offset的變量volatile語義的當前值,并設置變量volatile語義的值為update
由以上代碼可知,首先(1)處的getLongvolatile獲取當前變量的值,然后使用CAS原子操作設置新值。這里使用while循環是考慮到,在多個線程同時調用的情況下CAS失敗時需要重試。
long getAndAddLong(Object obj, long offset, long addValue)
獲取對象obj中偏移量為offset的變量volatile語義的當前值,并設置變量值為原始值+addValue
類似getAndSetLong的實現,只是這里進行CAS操作時使用了原始值+傳遞的增量參數addValue的值。
使用Unsafe類
Unsafe 這個類如此厲害,試一試???
import sun.misc.Unsafe;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/28 20:05* @mark: show me the code , change the world*/ public class TestUnSafe {// 1 獲取 Unsafe的實例static final Unsafe unsafe = Unsafe.getUnsafe();// 2 記錄變量 state在類 Testunsafe中的偏移值static final long stateoffset;// 3 變量private volatile long state = 0;static {try {// 4 獲取state變量在類TestUnsafe中的偏移量stateoffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));} catch (Exception e) {System.out.println(e.getLocalizedMessage());throw new Error(e);}}public static void main(String[] args) {// 5. 創建實例TestUnSafe testUnSafe = new TestUnSafe();// 6 設置state值為1 通過cas算法boolean b = unsafe.compareAndSwapLong(testUnSafe, stateoffset, 0, 1);System.out.println(b);}}-
代碼1 獲取了Unsafe的一個實例
-
代碼3創建了一個變量state并初始化為0。
-
代碼4使用unsafe.objectFieldOffset獲取TestUnSafe類里面的state變量,在TestUnSafe對象里面的內存偏移量地址并將其保存到stateOffset變量中。
-
代碼6 調用創建的unsafe實例的compareAndSwapInt方法,設置test對象的state變量的值。具體意思是,如果test對象中內存偏移量為stateOffset的state變量的值為0,則更新該值為1。
運行上面的代碼,我們期望輸出true,然而執行后會輸出如下結果
看看getUnsafe的代碼吧
繼續看下
// 9 判斷是不是BootStrap類加載器加載的 public static boolean isSystemDomainLoader(ClassLoader var0) {return var0 == null;}-
代碼7獲取調用getUnsafe這個方法的對象的Class對象,這里是TestUnSafe.class。
-
代碼8判斷是不是Bootstrap類加載器加載的localClass,在這里是看是不是Bootstrap加載器加載了TestUnSafe.class。很明顯由于TestUnSafe.class是使用AppClassLoader加載的,所以這里直接拋出了異常。
思考一下,這里為何要有這個判斷? 我們知道Unsafe類是rt.jar包提供的,rt.jar包里面的類是使用Bootstrap類加載器加載的,而我們的啟動main函數所在的類是使用AppClassLoader加載的,所以在main函數里面加載Unsafe類時,根據委托機制,會委托給Bootstrap去加載Unsafe類。
如果沒有代碼8的限制,那么我們的應用程序就可以隨意使用Unsafe做事情了,而Unsafe類可以直接操作內存,這是不安全的,所以JDK開發組特意做了這個限制,不讓開發人員在正規渠道使用Unsafe類,而是在rt.jar包里面的核心類中使用Unsafe功能。
如果開發人員真的想要實例化Unsafe類,那該如何做?
方法有多種,既然從正規渠道訪問不了,那么就玩點黑科技,使用萬能的反射來獲取Unsafe實例方法。
import sun.misc.Unsafe;import java.lang.reflect.Field;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/28 20:05* @mark: show me the code , change the world*/ public class TestUnSafe {// 1 獲取 Unsafe的實例static final Unsafe unsafe ;// 2 記錄變量 state在類 Testunsafe中的偏移值static final long stateoffset;// 3 變量private volatile long state = 0;public long getState() {return state;}static {try {// 使用反射獲取Unsafe成員變量theUnsafeField field = Unsafe.class.getDeclaredField("theUnsafe");// 設置為可存取field.setAccessible(true);// 獲取該變量的值unsafe = (Unsafe)field.get(null);// 獲取state在TestUnSfate中的偏移量stateoffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state"));} catch (Exception e) {System.out.println(e.getLocalizedMessage());throw new Error(e);}}public static void main(String[] args) {// 5. 創建實例TestUnSafe testUnSafe = new TestUnSafe();System.out.println("修改前:" + testUnSafe.getState());// 6 設置state值為1 通過cas算法boolean b = unsafe.compareAndSwapLong(testUnSafe, stateoffset, 0, 1);System.out.println("修改 " + b);System.out.println("修改前:" + testUnSafe.getState());}}在如上代碼中,通過反射獲取unsafe的實例, 運行后輸出結果如下。
總結
以上是生活随笔為你收集整理的Java Review - 并发编程_Unsafe的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java Review - 并发编程_锁
- 下一篇: Java Review - 并发编程_伪