likes
comments
collection
share

原子包-对象AtomicReference

作者站长头像
站长
· 阅读数 15

官方文档-AtomicReference

public class AtomicReference<V>
extends Object
implements Serializable

An object reference that may be updated atomically. See the java.util.concurrent.atomic package specification for description of the properties of atomic variables. 可以自动更新的对象引用。有关原子变量属性的描述,请参阅 java.util.concurrent.atomic 包规范。

AtomicReference作用

AtomicReference是Java中的一个原子类,它的主要作用是提供了一种原子操作的方式来更新对象的引用。它通常用于多线程环境下,用来解决并发访问共享对象时可能出现的竞态条件(race condition)问题。

具体来说,AtomicReference类提供了以下主要作用:

  1. 原子性更新引用: AtomicReference可以确保在多线程环境下,对对象引用的更新操作是原子的,即要么更新成功,要么失败,不存在中间状态。这有助于避免多线程同时修改同一对象引用时可能出现的数据不一致性问题。

  2. 解决竞态条件问题: 在多线程编程中,如果多个线程同时尝试更新共享对象的引用,可能会导致不可预测的结果。使用AtomicReference可以防止这种情况发生,确保只有一个线程能够成功更新引用。

  3. 实现非阻塞算法: AtomicReference可以用于实现非阻塞算法,这些算法通常比基于锁的算法具有更好的性能和可伸缩性,特别是在高并发情况下。

  4. 并发数据结构的实现: AtomicReference常常用于实现各种并发数据结构,如并发队列、并发映射等,以确保对这些数据结构的操作是线程安全的。

总之,AtomicReference是一个重要的多线程编程工具,用于确保对共享对象引用的操作是线程安全的,从而帮助开发人员编写更可靠和高效的多线程代码。它在Java中的应用广泛,特别是在需要处理并发访问共享数据的情况下。

应用场景

AtomicReference在多线程编程中有许多应用场景,其中一些常见的包括:

  1. 单例模式的实现: 如果要实现线程安全的单例模式,可以使用AtomicReference来确保只有一个实例被创建。多个线程尝试创建实例时,只有一个线程会成功,其他线程会失败。

  2. 状态切换: 在某些情况下,你可能需要在多线程环境下切换对象的状态。AtomicReference可以用于确保状态切换是原子的,不会出现竞态条件。

  3. 非阻塞算法: AtomicReference可用于实现各种非阻塞数据结构,如非阻塞队列、非阻塞堆栈等。这些数据结构通常比基于锁的数据结构具有更好的性能和可伸缩性。

  4. 并发集合的实现: 你可以使用AtomicReference来实现自定义的并发集合,如并发映射、并发列表等,以确保多线程下的安全访问。

  5. 原子更新对象: 如果你有一个可变的对象,需要在多线程环境下进行原子更新,AtomicReference可以用来管理这个对象的引用,确保更新是原子性的。

  6. 实现乐观锁: AtomicReference可用于实现乐观锁的机制,其中多个线程可以同时读取某个共享资源,但只有在执行更新操作时才检查是否有冲突。

  7. 协调多个线程的状态: 在某些多线程协作的场景中,你可能需要协调多个线程的状态或信号。AtomicReference可以用来存储和共享状态信息。

总之,AtomicReference在需要确保对象引用的原子性操作以及避免竞态条件的多线程应用中非常有用。它提供了一种高效且线程安全的方式来管理共享对象引用,有助于编写可靠的多线程代码。

举例

以下是一些使用AtomicReference的示例场景:

  1. 线程安全的单例模式:
public class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();

    private Singleton() { }

    public static Singleton getInstance() {
        while (true) {
            Singleton current = INSTANCE.get();
            if (current != null) {
                return current;
            }

            current = new Singleton();
            if (INSTANCE.compareAndSet(null, current)) {
                return current;
            }
        }
    }
}

这个示例中,通过AtomicReference来确保只有一个Singleton实例被创建,多个线程尝试创建实例时不会发生竞态条件。

  1. 非阻塞队列:
import java.util.concurrent.atomic.AtomicReference;

public class NonBlockingQueue<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;
    private AtomicReference<Node<T>> tail;

    public NonBlockingQueue() {
        Node<T> dummyNode = new Node<>(null);
        head = new AtomicReference<>(dummyNode);
        tail = new AtomicReference<>(dummyNode);
    }

    public void enqueue(T item) {
        Node<T> newNode = new Node<>(item);
        while (true) {
            Node<T> currentTail = tail.get();
            Node<T> tailNext = currentTail.next.get();
            if (currentTail == tail.get()) {
                if (tailNext != null) {
                    // Queue is in intermediate state; advance tail
                    tail.compareAndSet(currentTail, tailNext);
                } else {
                    // In the quiescent state; try to insert new node
                    if (currentTail.next.compareAndSet(null, newNode)) {
                        // Insertion succeeded; try to advance tail
                        tail.compareAndSet(currentTail, newNode);
                        return;
                    }
                }
            }
        }
    }

    // Dequeue and other methods can be similarly implemented
}

这个示例展示了如何使用AtomicReference来实现非阻塞队列,多个线程可以并发地执行enqueue操作而不会产生竞态条件。

这些示例突出了AtomicReference在处理多线程并发问题时的实际应用。它提供了一种原子性的方式来操作对象引用,确保线程安全,避免了锁的竞争。

实现原理

AtomicReference的实现原理涉及到底层的CPU指令和硬件支持,以确保操作对象引用的原子性。其实现原理主要依赖于CPU提供的CAS(Compare-And-Swap)指令或者类似的原子性操作。

CAS是一种原子性操作,它可以在不使用锁的情况下更新一个变量的值。CAS操作包括三个操作数:内存位置(通常是一个引用),期望的原值,以及新值。操作会在内存位置的值等于期望的原值时,将内存位置的值更新为新值,否则不执行更新操作。CAS操作是原子的,意味着在多线程环境下,只有一个线程能够成功执行CAS操作,其他线程的操作会失败。

AtomicReference内部使用CAS操作来实现对对象引用的原子操作。它通常包括以下几个关键部分:

  1. 内部数据结构: AtomicReference会包含一个内部的引用变量,用来存储对象引用。这个引用变量通常是volatile修饰的,以确保可见性,即多个线程能够看到最新的值。

  2. CAS操作: 当需要更新引用时,AtomicReference会使用CAS操作。它首先获取当前的引用值,然后与期望的原值进行比较。如果两者相等,表示没有其他线程在此期间修改过引用,那么就将新值设置进去。如果比较失败,表示有其他线程修改了引用,CAS操作会失败,需要重试或者放弃更新。

  3. 循环重试: 由于CAS操作可能会失败,因此AtomicReference通常会使用循环来不断尝试更新,直到成功为止。这确保了更新的原子性和线程安全性。

  4. 硬件支持: CAS操作的实现依赖于硬件的支持。现代处理器提供了CAS指令的原生支持,这些指令是原子性的,可以保证在多核CPU上的正确执行。

总之,AtomicReference的实现原理基于CAS操作,它使用硬件提供的原子性操作来确保对对象引用的操作是线程安全的。这使得多线程环境下的共享对象引用可以被安全地更新,而不需要显式地使用锁。这种方式提高了多线程编程的性能和可伸缩性。

源码分析

java.util.concurrent.atomic.AtomicReference#compareAndSet

/**
 * Atomically sets the value to the given updated value
 * if the current value {@code ==} the expected value.
 * @param expect the expected value
 * @param update the new value
 * @return {@code true} if successful. False return indicates that
 * the actual value was not equal to the expected value.
 */
public final boolean compareAndSet(V expect, V update) {
    return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}

说白了,也是基于cas和valotile。只不过是对象版本的原子类,之前讲过int版本的原子类AtomicInteger。道理是一样,原理也是一样。


数据字段

private volatile V value;

也是基于volatile。只不过这个时候的数据类型是对象。

转载自:https://juejin.cn/post/7278982293708210187
评论
请登录