Java 的 java.util.concurrent 包除了提供底层锁、并发集合外,还提供了一组原子操作的封装类,它们位于 java.util.concurrent.atomic 包。
AtomicInteger 为例,它提供的主要操作有:

  • 增加值并返回新值:int addAndGet(int delta)
  • 加 1 后返回新值:int incrementAndGet()
  • 获取当前值:int get()
  • 用 CAS 方式设置:int compareAndSet(int expect, int update)

Atomic 类是通过无锁(lock-free)的方式实现的线程安全(thread-safe)访问。它的主要原理是利用了 CAS:Compare and swap。
自己通过 CAS 编写 incrementAndGet(),它大概长这样:

  1. public int incrementAndGet(AtomicInteger var) {
  2. int current, next;
  3. do {
  4. current = var.get(); // 获取当前值
  5. next = prev + 1; // 当前值加 1
  6. } while ( ! var.compareAndSwap(current, next)); // 如果 AtomicInteger 的当前值是 current,那就返回 next。否则继续循环等待
  7. return next;
  8. }

通过 CAS 和 do...while 循环配合,即使其他线程修改了 AtomicInteger 的值,最终的结果也是正确的。

虽然上面使用 do...while 看上去很麻烦,但是实际过程中比使用锁的速度更快。

可以利用 AtomicLong 可以编写一个多线程安全的全局唯一 ID 生成器:

  1. class IdGenerator {
  2. AtomicLong var = new AtomicLong(0);
  3. public long getNextId() {
  4. return var.incrementAndGet();
  5. }
  6. }

通常情况下,我们并不需要直接用 do ... while 循环调用 compareAndSet 实现复杂的并发操作,而是用 incrementAndGet() 这样的封装好的方法,因此,使用起来非常简单。
如果有大量线程要访问相同的原子值,性能会大幅下降,因为乐观更新需要太多次重试。Java SE 8 提供了 LongAdderLongAccumulator 类来解决这个问题。
LongAdder 包括多个变量(加数),其总和为当前值。可以有多个线程更新不同的加数,线程个数增加时会自动提供新的加数。通常情况下,只有当所有工作都完成之后才需要总和的值,对于这种情况,这种方法会很高效。性能会有显著的提升。
这里写一个使用小例子:

  1. final LongAdder adder = new LongAdder();
  2. for(...) {
  3. pool.submit(() -> {
  4. while(...) {
  5. ...
  6. if (...) adder.incerment(); // 自增操作
  7. }
  8. });
  9. }
  10. ...
  11. long total = adder.sum(); // 获取总数

LongAccumulator 将这种思想推广到任意的累加操作。在构造器中,可以提供这个操作以及它的零元素

  1. LongAccumulator adder = new LongAccumulator(Long::sum, 0); // 操作符为 + ,零元素为 0
  2. // In some thread...
  3. adder.accumulate(value);

在内部,这个累加器包含变量 a1,a2,…,an。每个变量初始化为零元素(这个例子中零元素为 0 )。
调用 accumulate() 并提供值 v 时,其中一个变量会以原子方式更新为 ai=ai op v,这里 op 是中缀形式的累加操作。在我们这个例子中,调用 accumulate() 会对某个 i 计算 ai=ai+v。
get() 的结果是 a1 op a2 op…op an。在我们的例子中,这就是累加器的总和:a1+a2+…+an。

DoubleAdderDoubleAccumulator 也采用同样的方式,只不过处理的是 double 值。

小结

使用 java.util.concurrent.atomic 提供的原子操作可以简化多线程编程:

  • 原子操作实现了无锁的线程安全;
  • 适用于计数器(Adder),累加器(Accumulator)等。