• 线程安全之原子性学习与CAS机制


    原子性指的是一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。

    synchronized 可以保证代码片段的原子性。

    synchronized相比于volatile在保证可见性的同时,也保证了原子性

    问题描述

      2个线程对变量i进行递增操作

    public class LockDemo2 {
        volatile int i = 0;
    
        public void add() {
            i++;
        }
    
        public static void main(String[] args) throws InterruptedException {
            LockDemo2 ld = new LockDemo2();
    
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 10000; j++) {
                        ld.add();
                    }
                }).start();
            }
            Thread.sleep(2000L);
            System.out.println(ld.i);
        }
    }

      上面代码再运行的时候,volatile关键字保证了可见性,但是没有保证操作变量i的原子性,结果会出现20000以外的错误结果。  

    public class LockDemo2 {
        int i = 0;
    
        public void add() {
            synchronized (this) {
                i++;
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            LockDemo2 ld = new LockDemo2();
    
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 10000; j++) {
                        ld.add();
                    }
                }).start();
            }
            Thread.sleep(2000L);
            System.out.println(ld.i);
        }
    }
    

      synchronized同步代码块在保证了可见性的同时,也保证了变量i的原子性,当自己线程在使用i变量时,其他线程都不能对变量i进行操作。

     CAS机制

      Compare and swap 比较和交换。属于硬件同步原语,处理器提供了基本内存操作的原子保证。

      CAS操作需要输入两个数值,一个旧数A(期望操作前的值)和一个新值B,在操作期间先比较下旧值有没有发生变化,如果没有发生变化,才换成新值,

    发生了变化则不交换

      JAVA中的sun.misc.Unsafe类,提供了几个方法来实现CAS

       通过Unsafe类来实现CAS机制

    public class LockDemo1 {
        volatile int value = 0;
    
        static Unsafe unsafe; // 直接操作内存,修改对象,数组内存....强大的API
        private static long valueOffset;
    
        static {
            try {
                // 反射技术获取unsafe值
                Field field = Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                unsafe = (Unsafe) field.get(null);
    
                // 获取到 value 属性偏移量(用于定于value属性在内存中的具体地址)
                valueOffset = unsafe.objectFieldOffset(LockDemo1.class
                        .getDeclaredField("value"));
    
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    
        public void add() {
            // TODO xx00
            // i++;// JAVA 层面三个步骤
            // CAS + 循环 重试
            int current;
            do {
                // 操作耗时的话, 那么 线程就会占用大量的CPU执行时间
                current = unsafe.getIntVolatile(this, valueOffset);
            } while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
            // 可能会失败
        }
    
        public static void main(String[] args) throws InterruptedException {
            LockDemo1 ld = new LockDemo1();
    
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 10000; j++) {
                        ld.add();
                    }
                }).start();
            }
            Thread.sleep(2000L);
            System.out.println(ld.value);
        }
    }  

    原子操作封装类 

    以上部分内容和图片均摘自网易云课堂

  • 相关阅读:
    @Schedule注解中的Cron表达式读取properties的方法
    antdv 使用单文件方式递归生成菜单
    git 新建分支并将代码推送到远程
    echarts 饼图 pie label 颜色自定义
    关于bootstrapValidator 表单校验remote出现两次重复提交才能验证通过问题处理
    bootstrap table实现x-editable的行单元格编辑及解决数据Empty的问题
    element-ui 弹出组件的遮罩层在弹出层dialog模态框的上面
    vue cli 3.0 用axios 调用本地json数据一直报404
    vue项目webpack打包部署到tomcat时,访问成功,但是刷新后页面报404
    select2的使用
  • 原文地址:https://www.cnblogs.com/shuzhixia/p/13335981.html
Copyright © 2020-2023  润新知