Java使用Unsafe類的示例詳解
Unsafe 對(duì)象提供了非常底層的,操作內(nèi)存、線程的方法,相當(dāng)于開(kāi)了后門(mén)。
在atomic類中CAS實(shí)現(xiàn)、LockSupport中park unpark的底層都調(diào)用了UnSafe中的方法。
UnSafe并不是說(shuō)線程不安全,而是說(shuō)操作內(nèi)存有可能會(huì)造成不安全問(wèn)題。
當(dāng)然對(duì)于開(kāi)發(fā)人員來(lái)說(shuō)
Unsafe 對(duì)象不能直接調(diào)用,只能通過(guò)反射獲得

通過(guò)反射獲得Unsafe對(duì)象
package com.dongguo.unsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @author Dongguo
* @date 2021/9/12 0012-21:32
* @description:
*/
public class UnsafeAccessor {
static Unsafe unsafe;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
static Unsafe getUnsafe() {
return unsafe;
}
public static void main(String[] args) {
Unsafe unsafe = getUnsafe();
System.out.println(unsafe);
}
}
運(yùn)行結(jié)果
使用Unsafe實(shí)現(xiàn) CAS 操作
package com.dongguo.unsafe;
import lombok.Data;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @author Dongguo
* @date 2021/9/12 0012-21:32
* @description:
*/
public class UnsafeAccessor {
static Unsafe unsafe;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
static Unsafe getUnsafe() {
return unsafe;
}
public static void main(String[] args) throws NoSuchFieldException {
Unsafe unsafe = getUnsafe();
System.out.println(unsafe);
Field id = Student.class.getDeclaredField("id");
Field name = Student.class.getDeclaredField("name");
// 獲得成員變量的偏移量
long idOffset = unsafe.objectFieldOffset(id);
long nameOffset = unsafe.objectFieldOffset(name);
Student student = new Student();
// 使用 cas 方法替換成員變量的值
unsafe.compareAndSwapInt(student, idOffset, 0, 20); // 返回 true 0為舊值 20為新值
unsafe.compareAndSwapObject(student, nameOffset, null, "張三"); // 返回 true 舊值為null,新值為張三
System.out.println(student);
}
}
@Data
class Student {
volatile int id;
volatile String name;
}
運(yùn)行結(jié)果
sun.misc.Unsafe@7ea987ac
Student(id=20, name=張三)
直接使用Unsafe類實(shí)現(xiàn)之前AtomicIntegerFieldUpdater中線程安全的原子整數(shù) BankAccount
在atomic中使用AtomicIntegerFieldUpdater實(shí)現(xiàn)money線程安全的原子整數(shù)
package com.dongguo.unsafe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* @author Dongguo
* @date 2021/9/7 0007-14:41
* 以一種線程安全的方式操作非線程安全對(duì)象的某些字段。
* 需求:
* 1000個(gè)人同時(shí)向一個(gè)賬號(hào)轉(zhuǎn)賬一元錢(qián),那么累計(jì)應(yīng)該增加1000元,
* 除了synchronized和CAS,還可以使用AtomicIntegerFieldUpdater來(lái)實(shí)現(xiàn)。
*/
class BankAccount {
private String bankName = "ACBC";
public volatile int money = 0;
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
public void transferMoney(BankAccount bankAccount) {
fieldUpdater.incrementAndGet(bankAccount);
}
}
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) {
BankAccount bankAccount = new BankAccount();
for (int i = 1; i <= 1000; i++) {
new Thread(() -> {
bankAccount.transferMoney(bankAccount);
}, String.valueOf(i)).start();
}
//暫停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bankAccount.money);
}
}
改為使用UnSafe實(shí)現(xiàn)money線程安全的原子整數(shù)
package com.dongguo.unsafe;
import sun.misc.Unsafe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* @author Dongguo
* @date 2021/9/7 0007-14:41
*/
class BankAccount {
private String bankName = "ACBC";
public volatile int money;
static final Unsafe unsafe;
static final long DATA_OFFSET;
static {
unsafe = UnsafeAccessor.getUnsafe();
try {
// money 屬性在 BankAccount 對(duì)象中的偏移量,用于 Unsafe 直接訪問(wèn)該屬性
DATA_OFFSET = unsafe.objectFieldOffset(BankAccount.class.getDeclaredField("money"));
} catch (NoSuchFieldException e) {
throw new Error(e);
}
}
public BankAccount(int money) {
this.money = money;
}
public void transferMoney(int amount) {
int oldValue;
while (true) {
// 獲取共享變量舊值,可以在這一行加入斷點(diǎn),修改 data 調(diào)試來(lái)加深理解
oldValue = money;
// cas 嘗試修改 data 為 舊值 + amount,如果期間舊值被別的線程改了,返回 false
if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue + amount)) {
return;
}
}
}
}
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) {
BankAccount bankAccount = new BankAccount(0);
for (int i = 1; i <= 1000; i++) {
new Thread(() -> {
bankAccount.transferMoney(1);
}, String.valueOf(i)).start();
}
//暫停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bankAccount.money);
}
}
運(yùn)行結(jié)果
1000
/暫停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bankAccount.money);
}
}
運(yùn)行結(jié)果
1000
到此這篇關(guān)于Java使用Unsafe類的文章就介紹到這了,更多相關(guān)Java使用Unsafe類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA中Javaweb項(xiàng)目圖片加載不出來(lái)解決方案
在IDEA中能夠正常的預(yù)覽到圖片,但是在生成項(xiàng)目的war包時(shí),項(xiàng)目的目錄結(jié)構(gòu)卻會(huì)發(fā)生變化,所以無(wú)法訪問(wèn)圖片,本文主要介紹了IDEA中Javaweb項(xiàng)目圖片加載不出來(lái)解決方案,感興趣的可以了解一下2023-10-10
SpringMVC整合kinfe4j及問(wèn)題解決分析
這篇文章主要為大家介紹了SpringMVC整合kinfe4j及問(wèn)題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
零基礎(chǔ)寫(xiě)Java知乎爬蟲(chóng)之進(jìn)階篇
前面幾篇文章,我們都是簡(jiǎn)單的實(shí)現(xiàn)了java爬蟲(chóng)抓取內(nèi)容的問(wèn)題,那么如果遇到復(fù)雜情況,我們還能繼續(xù)那么做嗎?答案當(dāng)然是否定的,之前的僅僅是入門(mén)篇,都是些基礎(chǔ)知識(shí),給大家練手用的,本文我們就來(lái)點(diǎn)高大上的東西2014-11-11
解決Java 部署Tomcat時(shí)使用jni和jna調(diào)用DLL文件的問(wèn)題
這篇文章主要介紹了解決Java 部署Tomcat時(shí)使用jni和jna調(diào)用DLL文件的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
Springboot中MyBatisplus使用IPage和Page分頁(yè)的實(shí)例代碼
這篇文章主要介紹了Springboot中MyBatisplus使用IPage和Page分頁(yè),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

