Java中的Unsafe工具類使用詳解
一、Unsafe是什么?
Unsafe是jdk提供的一個(gè)直接訪問(wèn)操作系統(tǒng)資源的工具類(底層c++實(shí)現(xiàn)),它可以直接分配內(nèi)存,內(nèi)存復(fù)制,copy,提供cpu級(jí)別的CAS樂(lè)觀鎖等操作。
Unsafe位于sun.misc包下,jdk中的并發(fā)編程包juc(java.util.concurrent)基本全部靠Unsafe實(shí)現(xiàn),由此可見其重要性。
二、Unsafe對(duì)象的獲取
查看源碼:
結(jié)論:
- Unsafe是餓漢式的單例模式
- 只允許被引導(dǎo)類加載器(BootstrapClassLoader)加載的類使用,查看源碼可以看到在獲取unsafe對(duì)象時(shí)會(huì)判斷調(diào)用類是否是系統(tǒng)類加載器加載的,所以我們無(wú)法在自己的類中直接通過(guò)Unsafe.getUnsafe()獲取unsafe對(duì)象。所以只能通過(guò)反射直接new一個(gè)或者將其內(nèi)部靜態(tài)成員變量theUnsafe獲取出來(lái)
public static void main(String[] args) throws Exception{ Class<Unsafe> unsafeClass = Unsafe.class; //方法一:通過(guò)反射構(gòu)造一個(gè)Unsafe對(duì)象 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,它是樂(lè)觀鎖的一種實(shí)現(xiàn)。假設(shè)主存值為pre,預(yù)期值為expect,想要更新成得值為update,當(dāng)且僅當(dāng)主存值pre等預(yù)期值expect時(shí),才將pre更新為update。
1、相關(guān)方法
在unsafe中,實(shí)現(xiàn)CAS算法通過(guò)cpu的原子指令cmpxchg實(shí)現(xiàn),它對(duì)應(yīng)的方法如下:
簡(jiǎn)單介紹下它使用的參數(shù):
- 第一個(gè)參數(shù) var1為內(nèi)存中要操作的對(duì)象
- 第二個(gè)參數(shù) var2為要操作的值的內(nèi)存地址偏移量
- 第三個(gè)參數(shù) var4為預(yù)期值
- 第四個(gè)參數(shù) var5 為想要更新成的值
為了方便理解,舉個(gè)栗子。類User有一個(gè)成員變量name。我們new了一個(gè)對(duì)象User后,就知道了它(User對(duì)象)在內(nèi)存中的起始值,而員變量name在對(duì)象中的位置偏移是固定的。這樣通過(guò)這個(gè)起始值和這個(gè)偏移量就能夠定位到成員變量name在內(nèi)存中的具體位置。
如何得出name在對(duì)象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; } } }
因?yàn)閮?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)存模型掌控的,所以使用需要及其小心:
之后用到的時(shí)候再進(jìn)行補(bǔ)充
五、線程調(diào)度
通過(guò)Unsafe還可以直接將某個(gè)線程掛起,這和調(diào)用Object.wait()方法作用是一樣的,但是效率確更高!
我們熟知的AQS(AbstractQueuedSynchronizer)內(nèi)部掛起線程使用了LockSupport,而LockSupport內(nèi)部依舊使用的是Unsafe:
到此這篇關(guān)于Java中的Unsafe工具類使用詳解的文章就介紹到這了,更多相關(guān)Unsafe工具類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)簡(jiǎn)單猜數(shù)字
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單猜數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05SpringBoot2.3定制錯(cuò)誤頁(yè)面的方法示例
這篇文章主要介紹了SpringBoot2.3定制錯(cuò)誤頁(yè)面的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08IDEA x64 exe文件打不開,bat能打開問(wèn)題
這篇文章主要介紹了IDEA x64 exe文件打不開,bat能打開問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Intellij IDEA 2017.3使用Lombok及常用注解介紹
這篇文章主要介紹了Intellij IDEA 2017.3使用Lombok及常用注解介紹,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09springboot中手動(dòng)提交事務(wù)的實(shí)現(xiàn)方法
手動(dòng)提交事務(wù)可以提供更靈活的控制,以便在分布式環(huán)境中處理事務(wù)的提交和回滾,本文就來(lái)介紹一下springboot中手動(dòng)提交事務(wù)的實(shí)現(xiàn)方法,感興趣的可以了解一下2024-01-01Java中InputSteam怎么轉(zhuǎn)String
面了一位實(shí)習(xí)生,叫他給我說(shuō)一下怎么把InputStream轉(zhuǎn)換為String,這種常規(guī)的操作,他竟然都沒(méi)有用過(guò)我準(zhǔn)備結(jié)合工作經(jīng)驗(yàn),整理匯集出了InputStream 到String 轉(zhuǎn)換的十八般武藝,助大家闖蕩 Java 江湖一臂之力,需要的朋友可以參考下2021-06-06淺談Java鎖的膨脹過(guò)程以及一致性哈希對(duì)鎖膨脹的影響
本文主要介紹了Java鎖的膨脹過(guò)程以及一致性哈希對(duì)鎖膨脹的影響,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02關(guān)于java數(shù)組與字符串相互轉(zhuǎn)換的問(wèn)題
這篇文章主要介紹了java數(shù)組與字符串相互轉(zhuǎn)換的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10