Java 并发编程:原子类(Atomic Classes)核心技术的深度解析

Java 并发编程:原子类(Atomic Classes)核心技术的深度解析

在高并发场景下,线程安全是一个重要的话题。Atomic 类通过高效的 CAS(Compare-And-Swap)机制,为开发者提供了一种无需锁的线程安全解决方案。本篇文章将系统讲解 Java 原子类的核心概念、常用成员、使用方法以及实际应用。


一、概述

什么是原子操作?

原子操作是指操作在执行过程中不可被中断,要么全部执行成功,要么全部失败。

为什么需要 Atomic 类?

  • 传统方式的局限性:
    • 使用 synchronizedReentrantLock 确保线程安全,但代价是性能的下降。
  • Atomic 类的优势:
    • 通过 CAS 实现非阻塞操作,性能更高。

二、Atomic 类的核心成员

1. Java 提供了一系列 Atomic 类,适用于基本类型、引用类型以及特定场景。

类名 描述
AtomicInteger int 类型的原子操作
AtomicLong long 类型的原子操作
AtomicBoolean 对布尔值的原子操作
AtomicReference 对引用类型的原子操作
AtomicStampedReference 带版本号的引用原子操作,解决 ABA 问题
AtomicMarkableReference 带布尔标记的引用原子操作

2. 各类核心方法详情

1. AtomicInteger 和 AtomicLong
方法名 描述 示例代码
get() 获取当前值 int value = atomicInteger.get();
set(int newValue) 设置新值(非原子操作,直接替换) atomicInteger.set(10);
lazySet(int newValue) 延迟设置新值,最终会在必要时更新 atomicInteger.lazySet(10);
compareAndSet(int expect, int update) CAS 操作:期望值与当前值相同时更新 atomicInteger.compareAndSet(5, 10);
getAndSet(int newValue) 设置新值,并返回设置前的值 int oldValue = atomicInteger.getAndSet(10);
getAndIncrement() 原子递增操作,返回递增前的值 int oldValue = atomicInteger.getAndIncrement();
incrementAndGet() 原子递增操作,返回递增后的值 int newValue = atomicInteger.incrementAndGet();
getAndDecrement() 原子递减操作,返回递减前的值 int oldValue = atomicInteger.getAndDecrement();
decrementAndGet() 原子递减操作,返回递减后的值 int newValue = atomicInteger.decrementAndGet();
addAndGet(int delta) 增加指定值,返回增加后的结果 int newValue = atomicInteger.addAndGet(5);
getAndAdd(int delta) 增加指定值,返回增加前的结果 int oldValue = atomicInteger.getAndAdd(5);
weakCompareAndSet(int expect, int update) 类似 compareAndSet,但不能保证成功(可能在高竞争下失败) atomicInteger.weakCompareAndSet(5, 10);

2. AtomicBoolean
方法名 描述 示例代码
get() 获取当前布尔值 boolean value = atomicBoolean.get();
set(boolean newValue) 设置布尔值(非原子操作,直接替换) atomicBoolean.set(true);
compareAndSet(boolean expect, boolean update) CAS 操作:期望值与当前值相同时更新 atomicBoolean.compareAndSet(false, true);
getAndSet(boolean newValue) 设置新值,并返回设置前的值 boolean oldValue = atomicBoolean.getAndSet(true);

3. AtomicReference
方法名 描述 示例代码
get() 获取当前引用 Object ref = atomicReference.get();
set(V newValue) 设置新引用值 atomicReference.set(newValue);
lazySet(V newValue) 延迟设置引用值,最终会在必要时更新 atomicReference.lazySet(newValue);
compareAndSet(V expect, V update) CAS 操作:期望值与当前值相同时更新 atomicReference.compareAndSet(oldValue, newValue);
getAndSet(V newValue) 设置新值,并返回设置前的值 Object oldRef = atomicReference.getAndSet(newValue);
weakCompareAndSet(V expect, V update) 类似 compareAndSet,但不能保证成功(可能失败) atomicReference.weakCompareAndSet(oldRef, newRef);

4. AtomicStampedReference
方法名 描述 示例代码
getReference() 获取当前引用值 Object ref = atomicStampedRef.getReference();
getStamp() 获取当前版本号 int stamp = atomicStampedRef.getStamp();
compareAndSet(V expectReference, V newReference, int expectStamp, int newStamp) CAS 操作:比较并更新引用值和版本号 atomicStampedRef.compareAndSet(oldRef, newRef, oldStamp, newStamp);
set(V newReference, int newStamp) 设置新值及版本号 atomicStampedRef.set(newRef, newStamp);
get(int[] stampHolder) 获取当前引用值和版本号(通过数组存储版本号) Object ref = atomicStampedRef.get(stampHolder);

5. AtomicMarkableReference
方法名 描述 示例代码
getReference() 获取当前引用值 Object ref = atomicMarkableRef.getReference();
isMarked() 获取当前标记状态 boolean isMarked = atomicMarkableRef.isMarked();
compareAndSet(V expectReference, V newReference, boolean expectMark, boolean newMark) CAS 操作:比较并更新引用值和标记状态 atomicMarkableRef.compareAndSet(oldRef, newRef, false, true);
set(V newReference, boolean newMark) 设置新值及标记状态 atomicMarkableRef.set(newRef, true);
get(boolean[] markHolder) 获取当前引用值和标记状态(通过数组存储标记状态) Object ref = atomicMarkableRef.get(markHolder);

3. 说明与应用场景

  1. 数值类型(AtomicIntegerAtomicLong
    • 常用场景:计数器、统计数据、自增 ID。
    • 关键方法:incrementAndGet(), addAndGet(), compareAndSet()
  2. 布尔类型(AtomicBoolean
    • 常用场景:线程安全的标志位、控制任务执行状态。
    • 关键方法:compareAndSet(), getAndSet()
  3. 引用类型(AtomicReference
    • 常用场景:无锁栈、队列、链表的实现。
    • 关键方法:compareAndSet()
  4. 带版本号的引用(AtomicStampedReference
    • 常用场景:解决 ABA 问题,如内存管理或任务调度。
    • 关键方法:compareAndSet()
  5. 带标记的引用(AtomicMarkableReference
    • 常用场景:标记对象状态,如垃圾回收标记。
    • 关键方法:compareAndSet()

4. 方法分类总结

  • 常用方法get(), set(), compareAndSet()
  • 扩展操作getAndIncrement(), incrementAndGet(), getAndAdd()
  • 特殊方法weakCompareAndSet()(弱版本 CAS),lazySet()(延迟更新)
  • 高级方法AtomicStampedReferenceAtomicMarkableReference 的多值操作

三、原子操作的底层原理

1. CAS(Compare-And-Swap)机制

CAS 是一种乐观锁机制,尝试更新值时会比较预期值和当前值是否一致。

工作流程:
  1. 读取当前值和预期值。
  2. 如果预期值与当前值一致,则更新值。
  3. 如果不一致,重新尝试,直至更新成功。
示例:CAS 伪代码
java 复制代码
while (true) {
    int current = getCurrentValue(); // 获取当前值
    int newValue = current + 1;     // 计算新值
    if (compareAndSet(current, newValue)) { // 如果当前值未被修改,更新为新值
        System.out.println("CAS 成功,更新后的值为: " + newValue);
        break;
    } else {
        System.out.println("CAS 失败,重试...");
    }
}
优势:
  • 非阻塞,性能高。
缺陷:
  • ABA 问题: 值被修改后又还原,但 CAS 检测不到。
  • 自旋开销: 在高竞争场景下可能导致性能下降。

四、各种 Atomic 类的使用方法

1. AtomicIntegerAtomicLong

常用方法
  • get() / set(value):获取或设置值。
  • incrementAndGet() / getAndIncrement():递增操作。
  • compareAndSet(expect, update):CAS 更新。
示例:计数器的实现
java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounter {
    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet();
    }

    public int getCount() {
        return counter.get();
    }

    public static void main(String[] args) {
        AtomicCounter atomicCounter = new AtomicCounter();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                atomicCounter.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数: " + atomicCounter.getCount());
    }
}

2. AtomicBoolean

示例:线程安全的标志位
java 复制代码
import java.util.concurrent.atomic.AtomicBoolean;

public class AtomicBooleanExample {
    private AtomicBoolean flag = new AtomicBoolean(false);

    public void toggle() {
        flag.set(!flag.get());
    }

    public boolean getFlag() {
        return flag.get();
    }

    public static void main(String[] args) {
        AtomicBooleanExample example = new AtomicBooleanExample();
        example.toggle();
        System.out.println("标志位: " + example.getFlag());
    }
}

3. AtomicReference

示例:线程安全的链表节点
java 复制代码
import java.util.concurrent.atomic.AtomicReference;

public class AtomicNode<T> {
    private AtomicReference<T> value;

    public AtomicNode(T initialValue) {
        value = new AtomicReference<>(initialValue);
    }

    public T getValue() {
        return value.get();
    }

    public void setValue(T newValue) {
        value.set(newValue);
    }
}

4. AtomicStampedReference

解决 ABA 问题
java 复制代码
import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicStampedExample {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(1, 0);

        int stamp = ref.getStamp();
        System.out.println("初始版本: " + stamp);

        ref.compareAndSet(1, 2, stamp, stamp + 1);
        System.out.println("新值: " + ref.getReference() + ",新版本: " + ref.getStamp());
    }
}

5. AtomicMarkableReference

带标记的引用操作
java 复制代码
import java.util.concurrent.atomic.AtomicMarkableReference;

public class AtomicMarkableExample {
    public static void main(String[] args) {
        AtomicMarkableReference<Integer> ref = new AtomicMarkableReference<>(1, false);

        boolean marked = ref.isMarked();
        System.out.println("初始标记: " + marked);

        ref.set(2, true);
        System.out.println("新值: " + ref.getReference() + ",新标记: " + ref.isMarked());
    }
}

五、实战案例

1.高并发计数器:

java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicCounterExample {
    // 线程安全的计数器
    private AtomicInteger counter = new AtomicInteger(0);

    // 递增操作
    public void increment() {
        int newValue = counter.incrementAndGet();
        System.out.println(Thread.currentThread().getName() + " 递增后计数: " + newValue);
    }

    // 获取当前计数
    public int getCount() {
        return counter.get();
    }

    // 重置计数
    public void reset() {
        counter.set(0);
        System.out.println("计数器已重置");
    }

    public static void main(String[] args) {
        AtomicCounterExample example = new AtomicCounterExample();

        // 创建任务:每个线程递增计数器 1000 次
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        };

        // 创建并启动线程
        Thread t1 = new Thread(task, "线程-1");
        Thread t2 = new Thread(task, "线程-2");
        t1.start();
        t2.start();

        // 等待线程完成
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印最终计数
        System.out.println("最终计数: " + example.getCount());

        // 重置计数器并再次验证
        example.reset();
        System.out.println("重置后计数: " + example.getCount());
    }
}
  1. 日志打印
    • increment 方法中打印当前线程名和递增后的计数值,便于观察线程行为。
  2. 计数器重置功能
    • 添加 reset 方法,用于将计数器重置为 0,并打印重置日志。
  3. 线程命名
    • 为线程指定名称,日志更具可读性。

运行示例

运行后输出示例(不同机器的线程调度顺序可能不同):

线程-1 递增后计数: 1
线程-2 递增后计数: 2
线程-1 递增后计数: 3
线程-2 递增后计数: 4
...
最终计数: 2000
计数器已重置
重置后计数: 0

扩展功能与测试

1. 多线程扩展

增加线程数量,模拟更高并发的场景:

java 复制代码
public static void main(String[] args) {
    AtomicCounterExample example = new AtomicCounterExample();
    int threadCount = 10;
    int incrementsPerThread = 1000;

    // 创建多个线程任务
    Runnable task = () -> {
        for (int i = 0; i < incrementsPerThread; i++) {
            example.increment();
        }
    };

    // 创建并启动线程
    Thread[] threads = new Thread[threadCount];
    for (int i = 0; i < threadCount; i++) {
        threads[i] = new Thread(task, "线程-" + i);
        threads[i].start();
    }

    // 等待所有线程完成
    for (Thread thread : threads) {
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 打印最终计数
    System.out.println("最终计数: " + example.getCount());
}

输出示例:

线程-0 递增后计数: 1
线程-1 递增后计数: 2
线程-2 递增后计数: 3
...
最终计数: 10000

2. 性能测试

对比 AtomicIntegersynchronized 在高并发场景下的性能。

添加 Synchronized 版本计数器
java 复制代码
class SynchronizedCounter {
    private int counter = 0;

    public synchronized void increment() {
        counter++;
    }

    public synchronized int getCount() {
        return counter;
    }
}
性能测试代码
java 复制代码
public static void testPerformance(String label, Runnable task, int threadCount) {
    Thread[] threads = new Thread[threadCount];
    long startTime = System.nanoTime();

    for (int i = 0; i < threadCount; i++) {
        threads[i] = new Thread(task);
        threads[i].start();
    }

    for (Thread thread : threads) {
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    long endTime = System.nanoTime();
    System.out.printf("%s 执行时间: %d 毫秒%n", label, (endTime - startTime) / 1_000_000);
}

public static void main(String[] args) {
    int threadCount = 10;
    int incrementsPerThread = 1000;

    // AtomicInteger 测试
    AtomicCounterExample atomicCounter = new AtomicCounterExample();
    testPerformance("AtomicInteger", () -> {
        for (int i = 0; i < incrementsPerThread; i++) {
            atomicCounter.increment();
        }
    }, threadCount);

    // Synchronized 测试
    SynchronizedCounter syncCounter = new SynchronizedCounter();
    testPerformance("Synchronized", () -> {
        for (int i = 0; i < incrementsPerThread; i++) {
            syncCounter.increment();
        }
    }, threadCount);
}
输出示例
AtomicInteger 执行时间: 12 毫秒
Synchronized 执行时间: 25 毫秒

3. 减法操作与复位
实现递减功能

AtomicCounterExample 中添加递减功能:

java 复制代码
public void decrement() {
    int newValue = counter.decrementAndGet();
    System.out.println(Thread.currentThread().getName() + " 递减后计数: " + newValue);
}
调用示例
example.decrement();
System.out.println("递减后计数: " + example.getCount());

总结

通过上述代码的完善和扩展,AtomicCounterExample 已支持以下功能:

  1. 线程安全的计数器 :通过 AtomicInteger 确保线程安全。
  2. 日志记录:实时记录每次操作,便于观察线程行为。
  3. 重置功能:支持计数器重置,便于重复使用。
  4. 多线程支持:支持高并发场景下的测试。
  5. 性能对比 :展示了 AtomicInteger 相对于 synchronized 的性能优势。
  6. 减法操作:提供递减和复位功能,扩展了计数器的适用范围。

2.无锁队列实现

生产者-消费者模型的无锁队列实现

java 复制代码
import java.util.concurrent.atomic.AtomicReference;

// 无锁队列实现
public class LockFreeQueue<T> {
    private static class Node<T> {
        T value;
        AtomicReference<Node<T>> next;

        Node(T value) {
            this.value = value;
            this.next = new AtomicReference<>(null);
        }
    }

    private AtomicReference<Node<T>> head = new AtomicReference<>(null);
    private AtomicReference<Node<T>> tail = new AtomicReference<>(null);

    public LockFreeQueue() {
        Node<T> dummy = new Node<>(null); // 哑节点,防止队列空指针问题
        head.set(dummy);
        tail.set(dummy);
    }

    // 入队操作
    public void enqueue(T value) {
        Node<T> newNode = new Node<>(value);
        while (true) {
            Node<T> currentTail = tail.get();
            Node<T> tailNext = currentTail.next.get();
            if (currentTail == tail.get()) {
                if (tailNext == null) {
                    // 只有当 tail.next 为空时才能插入
                    if (currentTail.next.compareAndSet(null, newNode)) {
                        // 插入成功后移动 tail
                        tail.compareAndSet(currentTail, newNode);
                        System.out.println("【生产】入队: " + value);
                        return;
                    }
                } else {
                    // tail 已过时,推进到最新位置
                    tail.compareAndSet(currentTail, tailNext);
                }
            }
        }
    }

    // 出队操作
    public T dequeue() {
        while (true) {
            Node<T> currentHead = head.get();
            Node<T> currentTail = tail.get();
            Node<T> nextNode = currentHead.next.get();
            if (currentHead == head.get()) {
                if (currentHead == currentTail) {
                    // 队列空时 tail 和 head 应相等
                    if (nextNode == null) {
                        System.out.println("【消费】队列为空");
                        return null; // 队列为空
                    }
                    // 修正 tail 指向
                    tail.compareAndSet(currentTail, nextNode);
                } else {
                    // 提取值并移动 head
                    T value = nextNode.value;
                    if (head.compareAndSet(currentHead, nextNode)) {
                        System.out.println("【消费】出队: " + value);
                        return value;
                    }
                }
            }
        }
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return head.get().next.get() == null;
    }

    // 主函数:生产者-消费者模型
    public static void main(String[] args) {
        LockFreeQueue<Integer> queue = new LockFreeQueue<>();

        // 生产者线程
        Runnable producer = () -> {
            for (int i = 0; i < 10; i++) {
                queue.enqueue(i);
                try {
                    Thread.sleep((long) (Math.random() * 100)); // 模拟生产延迟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // 消费者线程
        Runnable consumer = () -> {
            while (true) {
                Integer value = queue.dequeue();
                if (value != null) {
                    try {
                        Thread.sleep((long) (Math.random() * 150)); // 模拟消费延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    try {
                        Thread.sleep(50); // 队列空时稍作等待,避免忙等待
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        // 启动生产者和消费者
        Thread producerThread = new Thread(producer, "生产者");
        Thread consumerThread1 = new Thread(consumer, "消费者1");
        Thread consumerThread2 = new Thread(consumer, "消费者2");

        producerThread.start();
        consumerThread1.start();
        consumerThread2.start();

        try {
            producerThread.join(); // 等待生产者完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

代码要点解析

  1. 无锁队列核心逻辑

    • 入队和出队操作都使用了 CAS(Compare-And-Set) 确保线程安全。

    • 哑节点

      的引入:

      • 防止队列初始为空时操作 headtail 引发空指针异常。
    • Tail 的推进机制:

      • 当队列插入失败时,通过 tail.compareAndSet 更新尾部节点,避免 tail 落后。
  2. 生产者-消费者模型

    • 使用生产者线程模拟数据生成,通过 enqueue 将数据加入队列。
    • 使用多个消费者线程,随机休眠后从队列中取数据。
  3. 队列空闲时的处理

    • dequeue 操作中,如果队列为空,消费者线程会稍作休眠以避免忙等待(浪费 CPU 资源)。
  4. 并发特性

    • 入队和出队操作完全独立,生产者与消费者无需直接交互即可实现数据传递。
    • 支持多个生产者和消费者线程同时操作队列。

运行输出示例

程序运行输出可能类似于以下内容(顺序因线程调度不同而异):

【生产】入队: 0
【生产】入队: 1
【消费】出队: 0
【生产】入队: 2
【消费】出队: 1
【消费】出队: 2
【消费】队列为空
【生产】入队: 3
【消费】出队: 3
【生产】入队: 4
【生产】入队: 5
【消费】出队: 4
【消费】出队: 5

3.解决 ABA 问题:

什么是 ABA 问题?
  • ABA 问题描述的是一个变量从 A 变为了 B,然后又变回了 A,但这种变化无法被普通的 CAS 操作检测到。
  • 对于普通的 AtomicReference,只比较值是否一致,无法感知值是否经历了中间变化。
AtomicStampedReference 的作用
  • 通过引入"版本号"(或时间戳),可以追踪变量的历史变化。
  • 每次 CAS 操作时,不仅比较当前值,还比较版本号。
代码功能
  • 初始值为 100,版本号为 0
  • 模拟 ABA 问题:值从 100 -> 200 -> 100,版本号从 0 -> 1 -> 2
  • 尝试更新时,通过版本号检测到值虽一致,但已被修改过,从而防止 ABA 问题。

代码实现

java 复制代码
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABAProblemSolver {
    public static void main(String[] args) {
        // 初始化 AtomicStampedReference
        AtomicStampedReference<Integer> atomicRef = new AtomicStampedReference<>(100, 0);

        // 获取初始值和版本号
        int initialStamp = atomicRef.getStamp();
        int initialValue = atomicRef.getReference();
        System.out.println("初始值: " + initialValue + ", 初始版本: " + initialStamp);

        // 模拟 ABA 问题
        atomicRef.compareAndSet(initialValue, 200, initialStamp, initialStamp + 1); // 100 -> 200
        atomicRef.compareAndSet(200, 100, initialStamp + 1, initialStamp + 2);     // 200 -> 100

        // 打印中间状态
        System.out.println("值: " + atomicRef.getReference() + ", 当前版本: " + atomicRef.getStamp());

        // 尝试用当前值更新版本号
        boolean success = atomicRef.compareAndSet(100, 300, atomicRef.getStamp(), atomicRef.getStamp() + 1);
        System.out.println("更新成功: " + success + ", 新值: " + atomicRef.getReference() + ", 新版本: " + atomicRef.getStamp());
    }
}

运行过程与输出

运行逻辑
  1. 初始状态
    • 值为 100,版本号为 0
  2. 模拟 ABA 问题
    • 第一次更新:值从 100 更新为 200,版本号从 0 变为 1
    • 第二次更新:值从 200 更新回 100,版本号从 1 变为 2
  3. 更新检测
    • CAS 操作尝试将 100 更新为 300
    • 因版本号已从初始版本 0 变为 2,更新失败,避免 ABA 问题。
输出示例
初始值: 100, 初始版本: 0
值: 100, 当前版本: 2
更新成功: false, 新值: 100, 新版本: 2

解决 ABA 问题的关键点

  1. 版本号检测
    • 每次更新值时,同时更新版本号。
    • CAS 操作时,需验证版本号是否一致,确保值未被其他线程修改。
  2. 线程安全性
    • 使用 AtomicStampedReference 确保操作的原子性。
    • 避免了值在中间状态被其他线程干扰的问题。

应用场景

  • 内存管理: 比如垃圾回收中的引用计数。
  • 任务调度: 在线程池中跟踪任务状态的变化。
  • 多线程协作: 检测任务是否已被其他线程抢占执行。

4.使用 AtomicBoolean 实现线程安全的单例模式:

java 复制代码
import java.util.concurrent.atomic.AtomicBoolean;

public class Singleton {
    private static Singleton instance;
    private static AtomicBoolean isInitialized = new AtomicBoolean(false);

    private Singleton() {
        // 私有构造函数
    }

    public static Singleton getInstance() {
        if (!isInitialized.get()) {
            synchronized (Singleton.class) {
                if (!isInitialized.get()) {
                    instance = new Singleton();
                    isInitialized.set(true);
                }
            }
        }
        return instance;
    }

    public void showMessage() {
        System.out.println("单例模式实例方法被调用");
    }

    public static void main(String[] args) {
        Runnable task = () -> {
            Singleton singleton = Singleton.getInstance();
            singleton.showMessage();
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
    }
}

6. 与其他并发工具的对比

1. 与 ReentrantLock 的对比

特性 Atomic ReentrantLock
性能 非阻塞操作,适合低竞争场景,性能高 阻塞操作,性能较低,但适合高竞争场景
复杂操作支持 仅支持单一变量的原子操作 支持复杂同步逻辑(如条件变量)
实现方式 基于 CAS,自旋重试 基于内核阻塞
使用场景 轻量级、高频操作,简单逻辑 高竞争场景,复杂逻辑的同步

示例场景:

  • Atomic 类适合用在高频调用的计数器等场景,例如访问计数、并发标志位操作。
  • ReentrantLock 适合复杂的多条件等待场景,如实现生产者-消费者模型。

2. 与 synchronized 的对比

特性 Atomic synchronized
性能 非阻塞操作,适合低竞争场景,性能高 阻塞操作,性能较低
使用难度 较复杂,需了解 CAS 原理 使用简单,语言级支持
线程安全范围 单一变量的线程安全操作 可保护整个方法或代码块的线程安全

适用场景选择:

  • 如果需要简单的线程安全操作,例如单个计数器的自增,可以使用 Atomic 类。
  • 如果需要保护多段代码的逻辑一致性或多个变量的同步,synchronized 更加适合。

7. 注意事项

1. 使用场景的误区

  • 多个变量操作的问题: Atomic 类不适合需要同时操作多个变量的场景。例如,在进行两个账户之间的转账时,不能只依靠 Atomic 类。 解决方案: 使用 ReentrantLockStampedLock 提供复杂同步支持。
  • 高竞争场景的性能问题: 在高竞争场景下,自旋操作可能导致 CPU 资源被过度消耗。 优化建议:
    • 限制自旋时间,必要时降级为阻塞锁。
    • 在可控场景下减少资源争用(如分段锁)。

2. 实现的最佳实践

  • 混合使用并发工具: 在复杂场景下,可以将 Atomic 类与锁机制结合使用。例如,使用 Atomic 类维护简单计数,锁机制控制复杂更新。
  • 谨慎选择适用范围: 确保 Atomic 类的使用只限于低竞争、高频操作的场景,例如计数器和状态标志。

9. 扩展知识

1. JDK 中 Atomic 类的源码分析

  • 底层实现:
    • Atomic 类通过 Unsafe 类的 compareAndSwapIntcompareAndSwapLong 方法实现。
    • 使用 CPU 的原子指令(如 cmpxchg)实现 CAS。
  • 内存屏障:
    • CAS 操作会触发内存屏障,确保变量的读写具有可见性。
    • volatile 关键字在 Atomic 类中被广泛使用,以保证变量的可见性。

2. Unsafe 类的作用

  • Unsafe 类是 JDK 提供的一个底层工具类,直接操作内存。
  • 常见方法:
    • compareAndSwapInt:执行 CAS 操作。
    • getObjectVolatile:获取变量的最新值,绕过缓存。
  • 注意:
    • Unsafe 是一个非公开 API,不建议在业务代码中直接使用。

3. Java 8 的改进:LongAdderLongAccumulator

  • LongAdder
    • 适合高并发场景下的计数操作。
    • 通过分段存储计数器,降低锁竞争。
  • LongAccumulator
    • 支持自定义的二元操作(如求和、最大值)。
    • 适合复杂的并发计算场景。

示例:LongAdder 的使用

java 复制代码
import java.util.concurrent.atomic.LongAdder;

public class LongAdderExample {
    public static void main(String[] args) {
        LongAdder adder = new LongAdder();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                adder.increment();
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("最终计数: " + adder.sum());
    }
}

通过对 Atomic 类及其实现原理、使用场景的深入理解,开发者可以在不同场景中灵活选择适合的并发工具,从而实现性能与安全的平衡。如果有其他需求,欢迎继续探讨!

相关推荐
爱学习的小羊啊9 分钟前
从零开始掌握Spring MVC:深入解析@Controller与@RequestMapping注解的使用
java·spring·mvc
小李不想输啦3 小时前
什么是微服务、微服务如何实现Eureka,网关是什么,nacos是什么
java·spring boot·微服务·eureka·架构
张铁铁是个小胖子3 小时前
微服务学习
java·学习·微服务
ggs_and_ddu3 小时前
Android--java实现手机亮度控制
android·java·智能手机
敲代码娶不了六花5 小时前
jsp | servlet | spring forEach读取不了对象List
java·spring·servlet·tomcat·list·jsp
Yhame.5 小时前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言
是小崔啊6 小时前
开源轮子 - EasyExcel02(深入实践)
java·开源·excel
myNameGL7 小时前
linux安装idea
java·ide·intellij-idea
青春男大7 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
HaiFan.7 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql