【多线程: Unsafe】

简介: 【多线程: Unsafe】

【多线程: Unsafe】

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

结果

sun.misc.Unsafe@4b1210ee
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实现的。

目录
相关文章
|
SQL 缓存 安全
JUC第十讲:CAS,Unsafe和原子类详解
JUC第十讲:CAS,Unsafe和原子类详解
JUC第十讲:CAS,Unsafe和原子类详解
|
7月前
|
安全 前端开发 Java
并发编程之原子操作Atomic&Unsafe
并发编程之原子操作Atomic&Unsafe
|
6月前
|
安全 Oracle Java
(四)深入理解Java并发编程之无锁CAS机制、魔法类Unsafe、原子包Atomic
其实在我们上一篇文章阐述Java并发编程中synchronized关键字原理的时候我们曾多次谈到过CAS这个概念,那么它究竟是什么?
137 1
|
8月前
|
存储 测试技术 Linux
使用unsafe与协程简单分析性能
【5月更文挑战第18天】本文档探讨了Go语言中使用标准库`unsafe`包与语言内置方式在类型转换(特别是`string`与`[]byte`之间)的性能差异。在涉及内存分配和复制的场景下,`unsafe`包能显著提升效率,但需深入理解其工作原理。
60 2
使用unsafe与协程简单分析性能
|
7月前
|
安全 Java 数据安全/隐私保护
深入理解java中Unsafe类及其实现原理
深入理解java中Unsafe类及其实现原理
151 0
|
8月前
|
Linux 调度 C语言
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)(上)
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)
79 0
|
8月前
|
安全 C语言 C++
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)(中)
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)
69 0
|
8月前
|
安全 Linux 编译器
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)(下)
从C语言到C++_40(多线程相关)C++线程接口+线程安全问题加锁(shared_ptr+STL+单例)
63 0
|
8月前
|
Java 编译器
多线程(volatile)
多线程(volatile)
37 0
|
Java API
【Unsafe】
【Unsafe】