zoukankan      html  css  js  c++  java
  • 线程安全之原子性学习与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);
        }
    }  

    原子操作封装类 

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

  • 相关阅读:
    【JavaScript&jQuery】$.ajax()
    【JavaScript&jQuery】5秒跳转
    【数据库_Mysql】Mysql知识汇总
    【Java】时间转json格式化
    【Java】数组升序和降序
    【Java】关于@RequestBody
    未能加载文件或程序集“Oracle.DataAccess”或它的某一个依赖项.试图加载格式不正确的程序
    IIS 7.0、IIS 7.5 和 IIS 8.0 中的 HTTP 状态代码 转
    在IIS7.5上添加.NET4.0程序的虚拟目录时提示ASP.NET 4.0尚未在 Web 服务器上注册
    VC2010 _com_error 返回的错误信息
  • 原文地址:https://www.cnblogs.com/shuzhixia/p/13335981.html
Copyright © 2011-2022 走看看