当前位置:网站首页>【面试:并发篇34:Unsafe】
【面试:并发篇34:Unsafe】
2022-07-29 22:19:00 【I cream】
【面试:并发篇34:Unsafe】
00.前言
如果有任何问题请指出,感谢。
01.介绍
Unsafe 对象提供了非常底层的,操作内存、线程的方法,Unsafe 对象不能直接调用,只能通过反射获得,因为Unsafe可以操控底层所以叫这个名字,并不是它线程不安全。
Unsafe源码

02.Unsafe CAS 操作
public class TestUnsafe {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe);
// 1. 获取域的偏移地址
long idOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("id"));
long nameOffset = unsafe.objectFieldOffset(Teacher.class.getDeclaredField("name"));
Teacher t = new Teacher();
// 2. 执行 cas 操作
unsafe.compareAndSwapInt(t, idOffset, 0, 1);
unsafe.compareAndSwapObject(t, nameOffset, null, "张三");
// 3. 验证
System.out.println(t);
}
}
@Data
class Teacher {
volatile int id;
volatile String name;
}
结果
[email protected]
Teacher(id=1, name=张三)
解释
我们通过反射获取到Unsafe类,通过反射获取到成员变量的地址的偏移量 然后用Unsafe对象 unsafe直接cas改变Teacher对象t的值,最终打印发现确实改变了,我们通过更加底层的Unsafe 进行cas操作改变其他类 而不是通过原子更新器进行操作的。
03.通过Unsafe模拟原子整数类型
介绍
我们用Unsafe模拟的原子整数类型进行之前做过的取钱例子
代码
Account接口
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/** * 方法内会启动 1000 个线程,每个线程做 -10 元 的操作 * 如果初始余额为 10000 那么正确的结果应当是 0 */ static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
long start = System.nanoTime();
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(account.getBalance()
+ " cost: " + (end-start)/1000_000 + " ms");
}
}
获取Unsafe对象的工具类
public class UnsafeAccessor {
private static final 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);
}
}
public static Unsafe getUnsafe() {
return unsafe;
}
}
模拟取钱例子
@Slf4j(topic = "c.Test42")
public class Test42 {
public static void main(String[] args) {
Account.demo(new MyAtomicInteger(10000));
}
}
class MyAtomicInteger implements Account {
private volatile int value;
private static final long valueOffset;
private static final Unsafe UNSAFE;
static {
UNSAFE = UnsafeAccessor.getUnsafe();
try {
valueOffset = UNSAFE.objectFieldOffset(MyAtomicInteger.class.getDeclaredField("value"));
} catch (NoSuchFieldException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public int getValue() {
return value;
}
public void decrement(int amount) {
while(true) {
int prev = this.value;
int next = prev - amount;
if (UNSAFE.compareAndSwapInt(this, valueOffset, prev, next)) {
break;
}
}
}
public MyAtomicInteger(int value) {
this.value = value;
}
@Override
public Integer getBalance() {
return getValue();
}
@Override
public void withdraw(Integer amount) {
decrement(amount);
}
}
结果
0 cost: 102 ms
解释
我们可以看出用Unsafe可以模拟原子整数类型 并且取钱例子没有线程安全问题,事实上大家可以看看原子类型的源码 全部都是用Unsafe实现的。
边栏推荐
猜你喜欢
随机推荐
华为畅享50 Pro评测:HarmonyOS加持 更流畅更安全
GBASE 8s 数据库复合索引
不要再用if-else!
趣味隐写术与密码术(现代密码学教程)
模型评价指标汇总(持续更新)
24-hour London gold chart analysis
GTK进行rgb绘图
An article to understand service governance in distributed development
GBASE 8s PAM插入式身份验证模块
一篇关于Web3.0如何走向安全时代的说明
【CVPR2022】A Unified Query-based Paradigm for Point Cloud Understanding
B. Party(图论/暴力/顶点的度数)
邮件服务器配置「建议收藏」
高等数学(第七版)同济大学 习题3-8 个人解答
jsonArray中按某字段排序
JZ23 链表中环的入口结点
文献综述的写作技巧,掌握这些技巧,效率大大提高!
杨辉三角的各种输出:
Advanced Mathematics (Seventh Edition) Tongji University Exercises 3-8 Individual Answers
JZ39 数组中出现次数超过一半的数字








