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

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

时间:2024-12-27 15:27:16浏览次数:7  
标签:Java Thread int Classes 线程 Atomic new public compareAndSet

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

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


一、概述

什么是原子操作?

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

为什么需要 Atomic 类?

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

二、Atomic 类的核心成员

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

类名描述
AtomicIntegerint 类型的原子操作
AtomicLonglong 类型的原子操作
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 伪代码
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 更新。
示例:计数器的实现
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

示例:线程安全的标志位
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

示例:线程安全的链表节点
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 问题
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

带标记的引用操作
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.高并发计数器:

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. 多线程扩展

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

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 版本计数器
class SynchronizedCounter {
    private int counter = 0;

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

    public synchronized int getCount() {
        return counter;
    }
}
性能测试代码
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 中添加递减功能:

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.无锁队列实现

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

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 问题。

代码实现

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 实现线程安全的单例模式:

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 的对比

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

示例场景:

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

2. 与 synchronized 的对比

特性Atomicsynchronized
性能非阻塞操作,适合低竞争场景,性能高阻塞操作,性能较低
使用难度较复杂,需了解 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 的使用

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

标签:Java,Thread,int,Classes,线程,Atomic,new,public,compareAndSet
From: https://blog.csdn.net/qq_36534560/article/details/144670534

相关文章

  • 12.25随笔 java实战2019年试题(2)
    后天上午就要期末考试了,最后尝试一下2019年试题Servlet设计与实现登录importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.h......
  • 12.24随笔 java实战2019年考题(1)
    距离期末考试还有三天,我最后把建民老师发的2019年考题进行一次上手解答,看看自己大概能拿到多少分。先建表房产基本信息表CREATETABLEhouse_basic_info(house_idVARCHAR(20)PRIMARYKEY,room_typeENUM('四室两厅','四室一厅','三室两厅','三室一厅','两室两厅','两......
  • 12.22 java实战 2019年考题(1)
    今天来实战建民老师发的2019年考试试题在IDE中创建一个Maven项目,项目的基本目录结构如下:src/main/java:用于存放Java源代码,包括Servlet、JavaBean、DAO等各类类src/main/resources:放置配置文件src/main/webapp:Web相关资源目录,包含HTML、CSS、JavaScript文件以及WEB-......
  • 12.23博客 java实战2019年试题
    今天继续完成建民老师发的2019年试题DAO层设计(数据访问对象,用于数据库操作)员工信息importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.util.ArrayList;......
  • Java性能提升的其他技巧
    Java性能提升的其他技巧虚拟线程Java19引入的一种轻量级线程,即虚拟线程(VirtualThread),并在Java21中正式GA。虚拟线程是Java21引入的一个新特性,用于简化并发编程。它与传统的操作系统线程相比,具有显著的优势:轻量级:虚拟线程由JVM管理,而非操作系统,因此它们的内存占......
  • 别再夹灰了!这份Java架构六大专题面试宝典值得你好好刷一刷
    Java面试是一个老生常谈的问题。每年到了金三银四&金九银十这种跳槽黄金季就会有一大批程序员出来面试找工作。流程就是熟悉的网上开始找面试题,面试手册,面试宝典,一收藏就是一大把,看到什么都觉得Nice,看几眼之后就收藏夹吃灰,对面试其实起不到什么实际性帮助。但其实只要你不做收......
  • JAVA-Day 02:注释
    JAVA中的三种注释1.单行注释单行注释格式为"//语句",在语句前添加两个斜杠"//"即可,如下图所示;publicclassexegesispublicstaticvoidmain(String[]args){//输出一个"Hello,world!"System.out.print("Hello,World!");}注意:单行注释只能注释一条语句!2.多......
  • JAVA-PTA题目集总结3
    ​1.前言    经过这两次对家具强电电路模拟的迭代,我对JAVA编程语言有了更深入的理解,同时在程序开发设计环节也有了很大的进步,吸收了上次编程练习的教训后,我在本阶段的编程练习中也有针对性的进行了改进。        本次的JAVA练习内容丰富,包括各种数据结构的应用......
  • 免费送源码(计算机毕业设计原创定制):Java+CSS+springboot Springboot高校医务室管理系统
    摘 要科技进步的飞速发展引起人们日常生活的巨大变化,电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流,人类发展的历史正进入一个新时代。在现实运用中,应用软件的工作规则和开发步骤,采用java技术建设高校......
  • 免费送源码:Java+springboot+MySQL 房屋租赁系统小程序的设计与实现 计算机毕业设计原
    目 录摘要11绪论11.1选题意义11.2开发现状11.3springboot框架介绍11.4论文结构与章节安排12 房屋租赁系统小程序系统分析32.1可行性分析32.1.1技术可行性分析32.1.2经济可行性分析32.1.3法律可行性分析32.2系统功能分析32.2.1功能性分析......