JAVA中相关原子操作详解 - 疫情不断,学习不断

原创
2020/02/23 22:15
阅读数 591

Java中的原子操作类详解

Java中的原子操作类详解

1、原子更新基本类型类  2、原子更新数组  3、原子更新引用类型  4、原子更新字段类  5、CAS算法  6、ABA问题

当更新一个变量时,如果多线程同时更新这个变量,为了保证多线程不会更新这个变量,一般使用关键字synchronized来保证多线程不会同时修改该变量。

jdk1.5后,java.util.concurrent.atomi包下提供了更简洁,高效和安全的原子操作类:

1、原子更新基本类型类

  • AtomicInteger:整型原子类

  • AtomicLong:长整型原子类

  • AtomicBoolean :布尔型原子类

  • AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免synchronized 的高开销,执行效率大为提升。

  • CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址。另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。

  • 源码:

       // setup to use Unsafe.compareAndSwapInt for updates
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
    
        static {
            try {
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }

     

  • 例子 

/**
 * 多线程环境不使用原子类保证线程安全(基本数据类型)
 * @author T-lih
 */
public class AtomicDemo1 {
    private volatile int count = 0;
    //若要线程安全执行执行count++,需要加锁
    public synchronized void incrementAdd() {
        count++;
    }
    public int getCount() {
        return count;
    }
}

/**
 * 多线程环境使用原子类保证线程安全(基本数据类型)
 */
public class AtomicDemo2 {
    private AtomicInteger count = new AtomicInteger();
    public void increment() {
        count.incrementAndGet();
    }
    //使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
    public int getCount() {
        return count.get();
    }
}

2、原子更新数组

  • AtomicIntegerArray:整型数组原子类

  • AtomicLongArray:长整型数组原子类

  • AtomicReferenceArray :引用类型数组原子类

3、原子更新引用类型

  • AtomicReference:引用类型原子类

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段

  • AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来

4、原子更新字段类

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器

  • AtomicLongFieldUpdater:原子更新长整型字段的更新器

  • AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

5、CAS算法

compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数

  • 需要读写的内存值 V

  • 进行比较的值 A

  • 拟写入的新值 B

当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试

6、ABA问题

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。

JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。


本学习要点,相关原子操作类方法使用。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部