【多线程: 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实现的。

目录
相关文章
|
11月前
|
SQL 缓存 安全
JUC第十讲:CAS,Unsafe和原子类详解
JUC第十讲:CAS,Unsafe和原子类详解
JUC第十讲:CAS,Unsafe和原子类详解
|
3月前
|
安全 前端开发 Java
并发编程之原子操作Atomic&Unsafe
并发编程之原子操作Atomic&Unsafe
|
2月前
|
安全 Oracle Java
(四)深入理解Java并发编程之无锁CAS机制、魔法类Unsafe、原子包Atomic
其实在我们上一篇文章阐述Java并发编程中synchronized关键字原理的时候我们曾多次谈到过CAS这个概念,那么它究竟是什么?
|
缓存 Java 编译器
【Java|多线程与高并发】volatile关键字和内存可见性问题
synchronized和volatile都是Java多线程中很重要的关键字,但它们的作用和使用场景有所不同。
|
缓存 Java 调度
【JavaEE】多线程之锁(synchronized)与volatile关键字
【JavaEE】多线程之锁(synchronized)与volatile关键字
|
缓存 安全 Java
Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
248 0
Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic
Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic
157 0
并发编程(八)Unsafe&Atomic
并发编程(八)Unsafe&Atomic
75 0
|
Java
CAS之什么是unsafe类(三)
CAS之什么是unsafe类(三)
196 0
CAS之什么是unsafe类(三)
Java——多线程高并发系列之线程间的通信(synchronized、Lock、Condition)
Java——多线程高并发系列之线程间的通信(synchronized、Lock、Condition)
Java——多线程高并发系列之线程间的通信(synchronized、Lock、Condition)