Bootstrap

【Java教程】Day15-16 多线程:线程同步——Java的原子操作类

在 Java 中,除了常见的底层锁和并发集合类,java.util.concurrent 包还提供了一组专门用于原子操作的封装类,位于 java.util.concurrent.atomic 包。通过这些类,我们可以在多线程环境下安全地进行无锁操作,避免了传统锁的性能开销。今天我们就来详细了解其中一个常用的类:AtomicInteger

1. 原子操作类概述

java.util.concurrent.atomic 包中的类提供了一些常见的数据类型(如 AtomicIntegerAtomicLong)的原子操作。这些类的核心特性是它们通过无锁(lock-free)方式实现线程安全,从而避免了传统同步机制的性能瓶颈。

我们以 AtomicInteger 为例,来了解其提供的一些常见操作:

1.1 主要操作
  • 增加值并返回新值int addAndGet(int delta)
    该方法将给定的增量 delta 加到当前值,并返回更新后的新值。

  • 加1后返回新值int incrementAndGet()
    该方法将当前值加1,并返回加1后的新值。

  • 获取当前值int get()
    该方法返回当前的值。

  • 使用CAS方式设置boolean compareAndSet(int expect, int update)
    该方法通过比较当前值是否为 expect,如果相等,则将其更新为 update,并返回 true,否则返回 false

1.2 CAS(Compare and Set)操作

AtomicInteger 以及其他原子类的底层实现基于 CAS(Compare and Set) 操作。CAS 操作是一种无锁的线程安全机制,其原理是:在操作过程中,它会比较当前值和预期值是否一致,如果一致,则更新为新值;如果不一致,说明另一个线程已修改了值,当前线程会重新获取最新的值。

举个例子,如果我们自己通过 CAS 编写 incrementAndGet() 方法,它大概长这样:

javapublic int incrementAndGet(AtomicInteger var) {    int prev, next;    do {        prev = var.get();  // 获取当前值        next = prev + 1;   // 增加1    } while (!var.compareAndSet(prev, next));  // CAS操作,直到成功    return next;  // 返回更新后的值}

CAS 操作保证了即使多个线程同时修改 AtomicInteger 的值,也能确保最终的更新是正确的。

2. 使用 AtomicLong 实现全局唯一ID生成器

AtomicLong 和 AtomicInteger 类似,用于提供对 long 类型数据的原子操作。我们可以使用它来实现一个线程安全的全局唯一 ID 生成器。例如:

javaclass IdGenerator {    AtomicLong var = new AtomicLong(0);    public long getNextId() {        return var.incrementAndGet();  // 线程安全的自增操作    }}

在这个例子中,AtomicLong 用于生成一个递增的全局唯一 ID。每次调用 getNextId() 时,var 会自增 1,并返回新值。

3. Java 8 中的新原子类:LongAdder 和 LongAccumulator

在高竞争场景下,AtomicInteger 和 AtomicLong 的性能可能会有所下降。为了解决这个问题,Java 8 引入了 LongAdder 和 LongAccumulator,这两个类在高并发情况下能提供更好的性能。

  • LongAdder:专为高并发环境设计,通过将数据分配到多个变量上,减少了线程间的竞争。它适用于高并发的累加场景。

  • LongAccumulator:与 LongAdder 类似,但它允许用户自定义累加操作。

这两个类的设计使得在高度竞争的环境下,也能保证性能的优化。

4. 小结

  • 原子操作:通过 java.util.concurrent.atomic 包提供的类(如 AtomicInteger 和 AtomicLong),我们可以实现无锁的线程安全操作,避免了传统锁带来的性能开销。

  • 适用场景:这些原子类适用于计数器、累加器等需要频繁更新的场景,尤其是在高并发环境下,它们的表现优于传统的同步机制。

  • 使用方便AtomicInteger 提供了简单易用的操作方法,如 incrementAndGet()compareAndSet() 等,使得多线程编程更加简洁高效。

通过使用原子类,开发者可以在保证线程安全的同时,提升程序的性能,避免了传统锁机制的瓶颈,特别是在高并发场景下,能够有效地提高系统的吞吐量。

;