• JUC P3 共享模型之无锁同步,CAS,原子类,Unsafe类 基础+代码


    JUC P3 共享模型之无锁同步,CAS,原子类,Unsafe类 基础+代码

    教程:https://www.bilibili.com/video/BV16J411h7Rd

    7. 共享模型之无锁

    • CASvolatile
    • 原子整数
    • 原子引用
    • 原子累加器
    • Unsafe

    7.1 取款问题

    创建 1000 个线程修改余额。

    7.1.1 上锁解决方案

    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            Account account = new AccountUnsafe(10000);
            Account.demo(account);
        }
    }
    
    class AccountUnsafe implements Account {
        private Integer balance;
    
        public AccountUnsafe(Integer balance) {
            this.balance = balance;
        }
    
        @Override
        public Integer getBalance() {
            synchronized (this) {
                return this.balance;
            }
        }
    
        @Override
        public void withdraw(Integer amount) {
            synchronized (this) {
                this.balance -= amount;
            }
        }
    }
    
    interface Account {
        /**
         * @return 查看余额
         */
        Integer getBalance();
    
        /**
         * @param amount 取款
         */
        void withdraw(Integer amount);
    
        /**
         * @param account 账户
         *                创建 1000 个线程取款
         */
        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) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println("最终余额: " + account.getBalance() + " cost: " + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述

    7.1.2 无锁解决方案

    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            Account account = new AccountCAS(10000);
            Account.demo(account);
        }
    }
    
    class AccountCAS implements Account {
        private AtomicInteger balance;
    
        public AccountCAS(Integer balance) {
            this.balance = new AtomicInteger(balance);
        }
    
        @Override
        public Integer getBalance() {
            return balance.get();
        }
    
        @Override
        public void withdraw(Integer amount) {
            while (true) {
                // 获取余额的最新值
                int latest = balance.get();
                // 修改余额
                int next = latest - amount;
                // 同步余额
                if (balance.compareAndSet(latest, next)) {
                    break;
                }
            }
        }
    }
    
    interface Account {
        /**
         * @return 查看余额
         */
        Integer getBalance();
    
        /**
         * @param amount 取款
         */
        void withdraw(Integer amount);
    
        /**
         * @param account 账户
         *                创建 1000 个线程取款
         */
        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) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println("最终余额: " + account.getBalance() + " cost: " + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    在这里插入图片描述

    稍微快一丢丢。

    7.2 CAS 与 volatile

    AtomicInteger 的解决方案中没有使用锁来保护共享变量的线程安全,那么它是怎么实现的线程安全?

    public void withdraw(Integer amount) {
    	while (true) {
    		int latest = balance.get();
    		int next = latest - amount;
    		if (balance.compareAndSet(latest, next)) {
    		    break;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    关键在于 compareAndSet,简称 CAS(Compare And Swap),它必须是原子操作

    AtomicInteger 源码中使用 volatile 关键字修饰变量保证变量可见性:
    在这里插入图片描述

    Note:
    CAS 的原子性是由操作系统保证的,操作系统中有 CAS 原语

    为什么无锁效率高

    • 无锁状态下,即使重试失败,线程始终在运行,没有停歇,而 synchronized 会让线程在没有获得锁的时候发生上下文切换,进入阻塞。

    Note:
    打个比喻:无锁状态下相当于线程都在高速上,一旦发生上下文切换,就好比赛车要减速,等待唤醒又要重新打火,启动加速,代价比较大。

    • 无锁状态下,线程要保持运行需要得到 CPU 支持,虽然不会进入阻塞,但是会因为没有得到时间片,一直处于就绪状态,还是会导致上下文切换

    CAS 特点

    结合 CASvolatile 可以实现无锁并发,适用于线程数较少、多核 CPU 的情景下。

    • CAS 是基于乐观锁的思想:不怕别的线程修改共享变量,若其他线程进行修改,当前线程会再重新尝试修改一下
    • synchronized 是基于悲观锁的思想:防止别的线程来修改共享变量,当前线程上了锁,其他线程无法进入

    CAS 体现的是无锁并发无阻塞并发

    • 因为没有 synchronized,因此线程不会陷入阻塞,这是效率提升的因素之一
    • 但是如果竞争激烈,重试会频繁发生,反而效率会受到影响

    7.3 原子整数

    在这里插入图片描述

    7.3.1 常用方法

    AtomicInteger i = new AtomicInteger(0); // 默认就是 0
    i.compareAndSet(0, 1); // 先看是不是 0, 若是 0, 则修改为 1
    log.debug("compareAndSet(0, 1) 之后的值: {}", i);
    
    log.debug("自增并获取值: {}", i.incrementAndGet()); // ++i
    log.debug("自减并获取值: {}", i.decrementAndGet()); // --i
    
    log.debug("先获取后自增: {}", i.getAndIncrement()); // i++
    log.debug("先获取后自减: {}", i.getAndDecrement()); // i--
    
    log.debug("先加再取值: {}", i.addAndGet(5));
    log.debug("先取值再加: {}", i.getAndAdd(5));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    简化取款操作
    public void withdraw(Integer amount) {
    	/*
    	while (true) {
    		int latest = balance.get();
    		int next = latest - amount;
    		if (balance.compareAndSet(latest, next)) {
    		    break;
    		}
    	}
    	*/
    	balance.getAndAdd(-1 * amount);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    7.3.2 自定义修改

    AtomicInteger i = new AtomicInteger(0); // 默认就是 0
    log.debug("自定义先修改, 然后返回: {}", i.updateAndGet(x -> x - 50));
    log.debug("自定义先返回, 然后修改: {}", i.getAndUpdate(x -> x * 20));
    log.debug("查看当前值: {}", i);
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    仿照着实现 updateAndGet 功能:

    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            AtomicInteger i = new AtomicInteger(5); // 默认就是 0
            
    //        log.debug("自定义先修改, 然后返回: {}", i.updateAndGet(x -> x * 20));
            log.debug("自定义先修改, 然后返回: {}", updateAndGet(i, x -> x * 20));
            log.debug("当前值: {}", i);
        }
    
        public static int updateAndGet(AtomicInteger i, IntUnaryOperator operator) {
            int prev, next;
            do {
                prev = i.get();
                next = operator.applyAsInt(prev);
            } while (!i.compareAndSet(prev, next));
            return next;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7.4 原子引用

    在这里插入图片描述

    7.4.1 AtomicReference 修改取款问题

    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            Account account = new AccountCAS(new BigDecimal("10000.0"));
            Account.demo(account);
        }
    }
    
    class AccountCAS implements Account {
        private final AtomicReference<BigDecimal> balance;
    
        public AccountCAS(BigDecimal balance) {
            this.balance = new AtomicReference<>(balance);
        }
    
        @Override
        public BigDecimal getBalance() {
            return balance.get();
        }
    
        @Override
        public void withdraw(BigDecimal amount) {
            balance.updateAndGet(x -> x.subtract(amount));
        }
    }
    
    interface Account {
        BigDecimal getBalance();
    
        void withdraw(BigDecimal amount);
    
        static void demo(Account account) {
            List<Thread> ts = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                ts.add(new Thread(() -> {
                    account.withdraw(BigDecimal.TEN);
                }));
            }
            long start = System.nanoTime();
    
            ts.forEach(Thread::start);
            ts.forEach(t -> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println("最终余额: " + account.getBalance() + " cost: " + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述

    7.4.2 ABA 问题

    如果共享变量经过修改后又恢复到原来的值,当前线程不能发现别的线程对该变量的修改过程:

    String prev = ref.get();
    
    new Thread(() -> {
        log.debug("change A -> B: {} ?", ref.compareAndSet("A", "B"));
        log.debug("change B -> A: {} ?", ref.compareAndSet("B", "A"));
    }, "t").start();
    
    TimeUnit.SECONDS.sleep(1);
    log.debug("change A -> C: {} ?", ref.compareAndSet(prev, "C"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    AtomicStampedReference 解决 ABA 问题

    AtomicStampedReference 可以解决上述问题,当前线程如果发现别的线程对该变量进行修改,则自己的 CAS 操作失败。

    通过设置一个版本号,可以追踪原子引用被修改了几次:

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
    
        public static void main(String[] args) throws InterruptedException {
            String prev = ref.getReference();
            int stamp = ref.getStamp();
            log.debug("当前版本号: {}", stamp);
    
            new Thread(() -> {
                int version = ref.getStamp();
                // 四个参数分别是,旧值,新值,旧版本号,新版本号
                log.debug("change A -> B: {} ?", ref.compareAndSet("A", "B", version, version + 1));
                log.debug("change B -> A: {} ?", ref.compareAndSet("B", "A", version + 1, version + 2));
            }, "t").start();
    
            TimeUnit.SECONDS.sleep(1);
            log.debug("当前版本号: {}", ref.getStamp());
            log.debug("change A -> C: {} ?", ref.compareAndSet(prev, "C", stamp, stamp + 1));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    AtomicMarkableReference 解决 ABA 问题

    有时候我们只关心变量是否被修改并不关心被修改了多少次,就可以使用 AtomicMarkableReference 来解决。

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        static AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", true);
    
        public static void main(String[] args) throws InterruptedException {
            String prev = ref.getReference();
    
            new Thread(() -> {
            	// 四个参数分别是,旧值,新值,旧标记,新标记
                log.debug("change A -> B ?: {}", ref.compareAndSet("A", "B", true, false));
                log.debug("change B -> A ?: {}", ref.compareAndSet("B", "A", false, false));
            }, "t").start();
    
            TimeUnit.SECONDS.sleep(1);
            log.debug("change A -> C ?: {}", ref.compareAndSet(prev, "C", true, false));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    7.5 原子数组

    在这里插入图片描述

    7.5.1 AtomicIntegerArray 配合 Lambda

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            demo(
                    () -> new int[10],
                    array -> array.length,
                    (array, index) -> array[index]++,
                    array -> System.out.println(Arrays.toString(array))
            );
    
            demo(
                    () -> new AtomicIntegerArray(10),
                    array -> array.length(),
                    (array, index) -> array.getAndIncrement(index),
                    array -> System.out.println(array)
            );
    
        }
    
        /**
         * @param arraySupplier 无中生有, 没有参数 -> 结果
         * @param lengthFun     一个参数, 一个结果, x -> 结果 ---- PS: BiFunction: 两个参数, 一个结果 (x, y) -> 结果
         * @param putConsumer   一个参数没结果 void
         * @param printConsumer 两个参数没结果 void
         * @param            泛型
         */
        private static <T> void demo(Supplier<T> arraySupplier,
                                     Function<T, Integer> lengthFun,
                                     BiConsumer<T, Integer> putConsumer,
                                     Consumer<T> printConsumer) {
            List<Thread> ts = new ArrayList<>();
            T array = arraySupplier.get();
            Integer length = lengthFun.apply(array);
    
            // 新建 length 个线程, 每个线程执行任务: 每个下标位置递增 10000 次
            for (int i = 0; i < length; i++) {
                ts.add(new Thread(() -> {
                    for (int j = 0; j < 10000; j++) {
                        putConsumer.accept(array, j % length);
                    }
                }));
            }
    
            ts.forEach(Thread::start);
            ts.forEach(t -> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            // 打印线程
            printConsumer.accept(array);
        }
    }
    
    • 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
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    在这里插入图片描述

    7.6 字段更新器

    在这里插入图片描述

    可以用来保护对象中属性赋值的原子性。

    7.6.1 AtomicReferenceFieldUpdater

    要求原子更新操作的属性必须声明为 volatile 保证可见性,否则会抛出异常 IllegalArgumentException: Must be volatile type

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            Student student = new Student();
            // 参数: 类,属性类型,属性名称
            AtomicReferenceFieldUpdater<Student, String> updater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
            log.debug(student.toString());
            log.debug("{}", updater.compareAndSet(student, null, "张三"));
            log.debug(student.toString());
        }
    }
    
    class Student {
        volatile String name;
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    7.7 原子累加器

    为什么要使用原子累加器?原子类不已经有了 incrementAndGet 方法了吗?

    • 从性能角度,原子累加器比普通的原子类的自增方法性能更高

    7.7.1 LongAdder 和 AtomicLong 比较

    LongAdder 累加器在有竞争的时候设置多个累加单元,Thread-0 累加 Cell[0],Thread-1 累加 Cell[1]…最后将结果汇总。累加时操作不同的 Cell 变量,减少了 CAS 重试失败,从而提高性能。

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            demo(
                    () -> new AtomicLong(0),
                    adder ->  adder.getAndIncrement()
            );
    
            demo(
                    () -> new LongAdder(),
                    adder -> adder.increment()
            );
        }
        private static <T> void demo (Supplier<T> adderSupplier, Consumer<T> action) {
            T adder = adderSupplier.get();
            List<Thread> ts = new ArrayList<>();
            for (int i = 0; i < 4; i++) {
                ts.add(new Thread(() -> {
                    for (int j = 0; j < 50_0000; j++) {
                        action.accept(adder);
                    }
                }));
            }
            long start = System.nanoTime();
            ts.forEach(Thread::start);
            ts.forEach(t -> {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println("总和: " + adder + " 花费时间: " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start) + " 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
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

    7.7.2 原理分析

    /**
     * Table of cells. When non-null, size is a power of 2.
     */
    transient volatile Cell[] cells; // 累加单元数组,懒初始化
    /**
    * Base value, used mainly when there is no contention, but also as
    * a fallback during table initialization races. Updated via CAS.
    */
    transient volatile long base; // 没有竞争时,用 CAS 累加
    /**
    * Spinlock (locked via CAS) used when resizing and/or creating Cells.
    */
    transient volatile int cellsBusy; // 在 cells 创建或者扩容时置为 1,表示加锁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    CAS 锁原理
    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            LockCas lockCas = new LockCas();
    
            new Thread(() -> {
                log.debug("begin...");
                lockCas.lock();
                try {
                    log.debug("lock...");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    lockCas.unlock();
                    log.debug("unlock...");
                }
            }).start();
    
            new Thread(() -> {
                log.debug("begin...");
                lockCas.lock();
                try {
                    log.debug("lock...");
                } finally {
                    lockCas.unlock();
                    log.debug("unlock...");
                }
            }).start();
        }
    }
    
    class LockCas {
        private AtomicInteger state = new AtomicInteger(0);
    
        public void lock() {
            while (true) {
                if (state.compareAndSet(0, 1)) {
                    break;
                }
            }
        }
    
        public void unlock() {
            state.set(0);
        }
    }
    
    • 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

    在这里插入图片描述

    Cell 类

    前情提要:

    • CPU 与内存之间速度差异很大,需要靠预读数据至 Cache 提升效率。
    • Cache 地单位是缓存行,每个缓存行对应一块内存,一般是 64Byte
    • Cache 的加入会造成数据副本产生,即同一份数据会缓存在不同核心地缓存行中,
    • CPU 要保证数据一致性,若某个 CPU 核心更改了数据,其他 CPU 核心对应的整个缓存行必须失败

    伪共享:因为 Cell 是数组形式的,在内存中是连续存储的,一个 Cell 为 24 字节(16 Byte 对象头 + 8 Byte value 属性),因此缓存行中可以存储 2 个 Cell 对象,这样就可能导致两个核心更新自己的 Cell 的时候让另外一个核心的缓存失效,需要重新读取内存。

    在这里插入图片描述

    @jdk.internal.vm.annotation.Contended 注解的作用:

    • 在此注解的对象或者字段前后增加 128 Byte 大小的 Padding,让 CPU 将对象预读到不同的缓存行,这样就不会造成对象缓存行的失效。
      在这里插入图片描述

    7.8 Unsafe

    7.8.1 概述

    Unsafe 对象提供了底层操作内存和线程的方法。

    Note:
    了解更多参考:
    https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
    https://www.cnblogs.com/throwable/p/9139947.html

    7.8.2 使用 Unsafe 类实现 CAS 修改属性

    Unsafe 不能通过 new 或者 Unsafe.getUnsafe 获取对象,只允许启动类加载器加载的类才可以使用。

    但是可以通过反射获取,使用 CAS 方法修改属性值:

    @Slf4j(topic = "c.InitTest")
    public class InitTest {
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            // 0. 通过反射获取 unsafe 对象
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafe.get(null);
    
            // 1. 获取域的偏移地址
            long idOffset = unsafe.objectFieldOffset(Student.class.getDeclaredField("id"));
            long nameOffset = unsafe.objectFieldOffset(Student.class.getDeclaredField("name"));
    
            Student student = new Student();
    
            // 2. 执行 CAS 操作
            unsafe.compareAndSwapInt(student, idOffset, 0, 100);
            unsafe.compareAndSwapObject(student, nameOffset, null, "王二麻子");
    
            log.debug(student.toString());
        }
    }
    
    @Data
    class Student {
        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

    7.8.3 模拟实现原子整数类

    参照 Atomic 源码实现,并测试:

    public class InitTest {
        public static void main(String[] args) throws InterruptedException {
            Account account = new AccountCAS(10000);
            Account.demo(account);
        }
    }
    
    class AccountCAS implements Account {
        private MyAtomicInteger balance;
    
        public AccountCAS(Integer balance) {
            this.balance = new MyAtomicInteger(balance);
        }
    
        @Override
        public Integer getBalance() {
            return balance.get();
        }
    
        @Override
        public void withdraw(Integer amount) {
            balance.addAndGet(-amount);
        }
    }
    
    interface Account {
        /**
         * @return 查看余额
         */
        Integer getBalance();
    
        /**
         * @param amount 取款
         */
        void withdraw(Integer amount);
    
        /**
         * @param account 账户
         *                创建 1000 个线程取款
         */
        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) {
                    throw new RuntimeException(e);
                }
            });
            System.out.println("最终余额: " + account.getBalance() + " cost: " + TimeUnit.NANOSECONDS.toMillis((System.nanoTime() - start)) + "ms");
        }
    }
    
    /**
     * 模拟 AtomicInteger 的实现
     */
    class MyAtomicInteger {
        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 MyAtomicInteger(int value) {
            this.value = value;
        }
    
        public final int get() {
            return value;
        }
    
        public final int addAndGet(int amount) {
            int prev, next;
            do {
                // Unsafe 底层就是这么写的, 可以通过类对象和偏移量获取属性值
    //            prev = UNSAFE.getIntVolatile(this, valueOffset);
                prev = value;
                next = prev + amount;
            } while (!UNSAFE.compareAndSwapInt(this, valueOffset, prev, next));
            return next;
    //        return UNSAFE.getAndAddInt(this, valueOffset, amount) + amount;
        }
    }
    
    /**
     * 反射获取 Unsafe 对象工具类
     */
    final class UnsafeAccessor {
        @SneakyThrows
        public static Unsafe getUnsafe() {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            ;
            theUnsafe.setAccessible(true);
            return (Unsafe) theUnsafe.get(null);
        }
    }
    
    • 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
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    在这里插入图片描述

  • 相关阅读:
    SpringCloud原理-OpenFeign篇(一、Hello OpenFeign项目示例)
    003 linux 自动化构建工具-make/makefile
    山东大学软件学院项目实训-创新实训-网络安全靶场实验平台(十一)
    【规范】看看人家Git提交描述,那叫一个规矩
    官方盘点 .NET 7 新功能
    Js逆向教程-09常见的加密方式
    数据结构堆详解
    2023-10-28 思考-精力管理-分析
    HTTP攻击,该怎么防护
    《数据资产管理实践白皮书》5.0版--数据资产管理发展趋势
  • 原文地址:https://blog.csdn.net/qq_39906884/article/details/127509686