• 【面试:并发篇34:Unsafe】


    【面试:并发篇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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    结果

    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");  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    获取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;  
        }  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    模拟取钱例子

    @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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    结果

    0 cost: 102 ms

    解释
    我们可以看出用Unsafe可以模拟原子整数类型 并且取钱例子没有线程安全问题,事实上大家可以看看原子类型的源码 全部都是用Unsafe实现的。

  • 相关阅读:
    使用Arduino和RFID制作基于指纹的汽车点火系统
    秋招每日一题T27——日期
    WPF 常用布局方式
    5分钟彻底搞懂this指向问题 (附练习题)
    开发工具 - Ubuntu版本VS code离线安装
    pytorch基本操作:使用神经网络进行分类任务
    UnityShader数学基础篇
    vue3-puzzle-vcode接入后端验证
    spring: HandlerInterceptor
    ES是什么?ES的使用场景有哪些?分词器??
  • 原文地址:https://blog.csdn.net/m0_71229547/article/details/126062213