七日打卡:Atomic

333 阅读3分钟

原子(atom)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。在多处理器上实现原子操作就变得 有点复杂。本文让我们一起来聊一聊在Inter处理器和Java里是如何实现原子操作的。

Atomic

  • 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更 新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装 类。
  • 基本类:AtomicInteger、AtomicLong、AtomicBoolean;
  • 引用类型:AtomicReference、AtomicReference的ABA实例、 AtomicStampedRerence、AtomicMarkableReference;
  • 数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray - 属性原子修改器(Updater):AtomicIntegerFieldUpdater、 AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
  • 1、原子更新基本类型类 用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:
    • AtomicBoolean:原子更新布尔类型。
    • AtomicInteger:原子更新整型。
    • AtomicLong:原子更新长整型。
      • AtomicInteger的常用方法如下:
        • int addAndGet(int delta) :以原子方式将输入的数值与实例中的值 (AtomicInteger里的value)相加,并返回结果
        • boolean compareAndSet(int expect, int update) :如果输入的数值等于预期 值,则以原子方式将该值设置为输入的值。
        • int getAndIncrement():以原子方式将当前值加1,注意:这里返回的是自增 前的值。void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值 后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
        • int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧 值。
      • Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和 double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是 使用Unsafe实现的,Unsafe只提供了三种CAS方法,compareAndSwapObject, compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是 先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double 也可以用类似的思路来实现。
  • 2、原子更新数组类 通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
    • AtomicIntegerArray:原子更新整型数组里的元素。
    • AtomicLongArray:原子更新长整型数组里的元素。
    • AtomicReferenceArray:原子更新引用类型数组里的元素。 - AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下:
      • int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相 加。
      • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期 值,则以原子方式将数组位置i的元素设置成update值。
  • 3、原子更新引用类型 原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变 量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:
    • AtomicReference:原子更新引用类型。
    • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
    • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更 新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)