Java中的Unsafe工具類使用詳解
一、Unsafe是什么?
Unsafe是jdk提供的一個直接訪問操作系統(tǒng)資源的工具類(底層c++實現(xiàn)),它可以直接分配內(nèi)存,內(nèi)存復(fù)制,copy,提供cpu級別的CAS樂觀鎖等操作。
Unsafe位于sun.misc包下,jdk中的并發(fā)編程包juc(java.util.concurrent)基本全部靠Unsafe實現(xiàn),由此可見其重要性。
二、Unsafe對象的獲取
查看源碼:
結(jié)論:
- Unsafe是餓漢式的單例模式
- 只允許被引導(dǎo)類加載器(BootstrapClassLoader)加載的類使用,查看源碼可以看到在獲取unsafe對象時會判斷調(diào)用類是否是系統(tǒng)類加載器加載的,所以我們無法在自己的類中直接通過Unsafe.getUnsafe()獲取unsafe對象。所以只能通過反射直接new一個或者將其內(nèi)部靜態(tài)成員變量theUnsafe獲取出來
public static void main(String[] args) throws Exception{ Class<Unsafe> unsafeClass = Unsafe.class; //方法一:通過反射構(gòu)造一個Unsafe對象 Constructor<Unsafe> constructor = unsafeClass.getDeclaredConstructor(); constructor.setAccessible(true); Unsafe unsafe1 = constructor.newInstance(); System.out.println(unsafe1); //方法二:獲取內(nèi)部靜態(tài)成員變量 Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe2 = (Unsafe) theUnsafe.get(null); System.out.println(unsafe2); }
三、CAS
CAS譯為Compare And Swap,它是樂觀鎖的一種實現(xiàn)。假設(shè)主存值為pre,預(yù)期值為expect,想要更新成得值為update,當且僅當主存值pre等預(yù)期值expect時,才將pre更新為update。
1、相關(guān)方法
在unsafe中,實現(xiàn)CAS算法通過cpu的原子指令cmpxchg實現(xiàn),它對應(yīng)的方法如下:
簡單介紹下它使用的參數(shù):
- 第一個參數(shù) var1為內(nèi)存中要操作的對象
- 第二個參數(shù) var2為要操作的值的內(nèi)存地址偏移量
- 第三個參數(shù) var4為預(yù)期值
- 第四個參數(shù) var5 為想要更新成的值
為了方便理解,舉個栗子。類User有一個成員變量name。我們new了一個對象User后,就知道了它(User對象)在內(nèi)存中的起始值,而員變量name在對象中的位置偏移是固定的。這樣通過這個起始值和這個偏移量就能夠定位到成員變量name在內(nèi)存中的具體位置。
如何得出name在對象User中的偏移量,Unsafe自然也提供了相應(yīng)的方法:
2、demo
import sun.misc.Unsafe; import java.lang.reflect.*; public class UnsafeDemo { public static void main(String[] args) throws Exception { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(null); User user = new User("jsbintask"); long nameOffset = unsafe.objectFieldOffset(User.class.getDeclaredField("name")); boolean res1 = unsafe.compareAndSwapObject(user, nameOffset, "jsbintask1", "jsbintask2"); System.out.println(res1+", 第一次更新后的值:" + user.getName()); boolean res2 = unsafe.compareAndSwapObject(user, nameOffset, "jsbintask", "jsbintask2"); System.out.println(res2+", 第二次更新后的值:" + user.getName()); } public static class User { private String name; public User(String name) { this.name = name; } public String getName() { return name; } } }
因為內(nèi)存中name的值為"jsbintask",而第一次使用compareAndSwapObject方法預(yù)期值為"jsbintask1",這顯然是不相等的,所以第一次更新失敗,返回false。第二次我們傳入了正確的預(yù)期值,返回true,更新成功!
四、內(nèi)存分配
Unsafe還給我們提供了直接分配內(nèi)存,釋放內(nèi)存,拷貝內(nèi)存,內(nèi)存設(shè)置等方法,值得注意的是,這里的內(nèi)存指的是堆外內(nèi)存!它是不受jvm內(nèi)存模型掌控的,所以使用需要及其小心:
之后用到的時候再進行補充
五、線程調(diào)度
通過Unsafe還可以直接將某個線程掛起,這和調(diào)用Object.wait()方法作用是一樣的,但是效率確更高!
我們熟知的AQS(AbstractQueuedSynchronizer)內(nèi)部掛起線程使用了LockSupport,而LockSupport內(nèi)部依舊使用的是Unsafe:
到此這篇關(guān)于Java中的Unsafe工具類使用詳解的文章就介紹到這了,更多相關(guān)Unsafe工具類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Intellij IDEA 2017.3使用Lombok及常用注解介紹
這篇文章主要介紹了Intellij IDEA 2017.3使用Lombok及常用注解介紹,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2019-09-09springboot中手動提交事務(wù)的實現(xiàn)方法
手動提交事務(wù)可以提供更靈活的控制,以便在分布式環(huán)境中處理事務(wù)的提交和回滾,本文就來介紹一下springboot中手動提交事務(wù)的實現(xiàn)方法,感興趣的可以了解一下2024-01-01Java中InputSteam怎么轉(zhuǎn)String
面了一位實習生,叫他給我說一下怎么把InputStream轉(zhuǎn)換為String,這種常規(guī)的操作,他竟然都沒有用過我準備結(jié)合工作經(jīng)驗,整理匯集出了InputStream 到String 轉(zhuǎn)換的十八般武藝,助大家闖蕩 Java 江湖一臂之力,需要的朋友可以參考下2021-06-06關(guān)于java數(shù)組與字符串相互轉(zhuǎn)換的問題
這篇文章主要介紹了java數(shù)組與字符串相互轉(zhuǎn)換的問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10