JUC原子类总结

38 阅读2分钟

此文是对JUC原子类总结,如有错漏欢迎在评论区指正;愿我,也愿在这个内卷时代努力提升自己的朋友们,坚持不懈,静待花开。

JUC原子类是JUC提供的一组具有原子操作特征的类,这些类都以Automic开头,核心思想是无锁编程和cas,主要是提供了一种高效且线程安全的无锁方式实现单个变量的原子操作。

JUC原子类分为三个大类

1. 基本类型原子类

AutomicInteger

AutomicLong

AutomicBoolean

2. 数组类型原子类

AutomicIntegerArray

AutomicLongArray

AutomicReferenceArray

3. 引用类型原子类

原子类的实现原理

以AutomicInteger为例,其内部有一个int类型的value属性,通过volatile关键字确保字段的内存可见性,在设置值和自增时,通过cas自旋更新value,如果cas操作成功,则返回旧值,否则更新旧值为最新值,继续尝试更新,直到成功为止,它没有自旋次数限制

应用场景

原子类主要适用于需要对单个变量进行原子性更新的场景,特别是读多写少的场景。

1. 计数器

2. 状态标识

public class TaskRunner {
    // 使用 volatile 可以保证可见性,但复合操作(如 check-then-act)仍需同步
    // private volatile boolean running = true;

    // 使用 AtomicBoolean 可以安全地进行原子性的状态检查和设置
    private AtomicBoolean running = new AtomicBoolean(true);

    public void stop() {
        running.set(false); // 原子性的写
    }

    public void run() {
        while (running.get()) { // 原子性的读
            // 执行任务...
        }
    }

    // 一个更复杂的操作:如果状态是 true,就设置为 false
    public boolean stopIfRunning() {
        return running.compareAndSet(true, false); // 原子性的“如果为真则设为假”
    }
}

3. 实现非阻塞算法(Non-blocking Algorithms)

这是原子类更高级的应用。利用 CAS 可以实现复杂的无锁数据结构,如无锁栈、无锁队列。ConcurrentLinkedQueue就是基于 CAS 实现的。

import java.util.concurrent.atomic.AtomicReference;

public class ConcurrentStack<E> {
    // 栈顶节点,使用原子引用
    private AtomicReference<Node<E>> top = new AtomicReference<>();

    public void push(E item) {
        Node<E> newHead = new Node<>(item);
        Node<E> oldHead;
        do {
            oldHead = top.get(); // 获取当前栈顶
            newHead.next = oldHead; // 新节点的next指向原栈顶
        } while (!top.compareAndSet(oldHead, newHead)); // CAS 替换栈顶,失败则重试
    }

    public E pop() {
        Node<E> oldHead;
        Node<E> newHead;
        do {
            oldHead = top.get();
            if (oldHead == null) {
                return null; // 栈为空
            }
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead)); // CAS 替换栈顶,失败则重试
        return oldHead.item;
    }

    private static class Node<E> {
        public final E item;
        public Node<E> next;

        public Node(E item) {
            this.item = item;
        }
    }
}