线程安全之原子性学习与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);
    }
}  

原子操作封装类 

 

 

 

 

 

 

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

posted @ 2020-07-18 15:42  树之下  阅读(251)  评论(0编辑  收藏  举报