Unsafe 对象提供了非常底层的,操作内存、线程的方法,相当于开了后门。
在atomic类中CAS实现、LockSupport中park unpark的底层都调用了UnSafe中的方法。
UnSafe并不是说线程不安全,而是说操作内存有可能会造成不安全问题。
当然对于开发人员来说
Unsafe 对象不能直接调用,只能通过反射获得
通过反射获得Unsafe对象
package com.dongguo.unsafe;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
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);
}
}
运行结果
sun.misc.Unsafe@7ea987ac
使用Unsafe实现 CAS 操作
package com.dongguo.unsafe;
import lombok.Data;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
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;
}
运行结果
sun.misc.Unsafe@7ea987ac
Student(id=20, name=张三)
直接使用Unsafe类实现之前AtomicIntegerFieldUpdater中线程安全的原子整数 BankAccount
在atomic中使用AtomicIntegerFieldUpdater实现money线程安全的原子整数
package com.dongguo.unsafe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
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实现money线程安全的原子整数
package com.dongguo.unsafe;
import sun.misc.Unsafe;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
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 对象中的偏移量,用于 Unsafe 直接访问该属性
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) {
// 获取共享变量旧值,可以在这一行加入断点,修改 data 调试来加深理解
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);
}
}
运行结果
1000
/暂停毫秒
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bankAccount.money);
}
}
运行结果
1000
到此这篇关于Java使用Unsafe类的文章就介绍到这了,更多相关Java使用Unsafe类内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!