Java使用Unsafe类的示例详解

网友投稿 298 2022-09-25


Java使用Unsafe类的示例详解

Unsafe 对象提供了非常底层的,操作内存、线程的方法,相当于开了后门。

在atomic类中CAS实现、LockSupport中park unpark的底层都调用了UnSafe中的方法。

UnSafe并不是说线程不安全,而是说操作内存有可能会造成不安全问题。

当然对于开发人员来说

Unsafe 对象不能直接调用,只能通过反射获得

通过反射获得Unsafe对象

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);

}

}

运行结果

sun.misc.Unsafe@7ea987ac

使用Unsafe实现 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;

}

运行结果

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;

/**

* @author Dongguo

* @date 2021/9/7 0007-14:41

* 以一种线程安全的方式操作非线程安全对象的某些字段。

* 需求:

* 1000个人同时向一个账号转账一元钱,那么累计应该增加1000元,

* 除了synchronized和CAS,还可以使用AtomicIntegerFieldUpdater来实现。

*/

class BankAccount {

private String bankName = "ACBC";

public volatile JkCulEint money = 0;

AtomicIntegerFieldUpdater 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;

/**

* @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();

JkCulE 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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:IPv6通讯原理(1) - 不能忽略的网卡启动过程(ipv6通信原理)
下一篇:子网掩码怎么口算?-【块计算方法】(子网划分掩码怎么算)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~