31.jdk源码阅读之AtomicReference

1. 写在前面

在这里插入图片描述
AtomicReference 是 Java 中提供的一种用于处理对象引用的原子操作类。它在并发编程中非常有用,可以确保对对象引用的操作是原子的,避免了使用锁的开销。下面的问题不知道大家在日常工作中是否思考过:

  1. AtomicReference 是如何实现原子操作的?
  2. AtomicReference 的 compareAndSet 方法是如何工作的?
  3. AtomicReference 的应用场景有哪些?
  4. AtomicReference 和 synchronized 的区别是什么?
  5. AtomicReference 是否支持泛型?
  6. AtomicReference 和 AtomicInteger 之间的区别是什么?

2. 从使用说起

2.1 基本使用

AtomicReference 可以用来实现对对象引用的原子更新。

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceBasicUsage {
    public static void main(String[] args) {
        AtomicReference<String> atomicString = new AtomicReference<>("initial value");

        // 获取当前值
        String currentValue = atomicString.get();
        System.out.println("Current Value: " + currentValue);

        // 更新值
        atomicString.set("new value");
        System.out.println("Updated Value: " + atomicString.get());

        // 比较并设置值
        boolean isUpdated = atomicString.compareAndSet("new value", "updated value");
        System.out.println("Is Updated: " + isUpdated);
        System.out.println("Final Value: " + atomicString.get());
    }
}

2.2 实现非阻塞栈

使用 AtomicReference 可以实现一个简单的非阻塞栈。

import java.util.concurrent.atomic.AtomicReference;

public class NonBlockingStack<T> {
    private static class Node<T> {
        final T value;
        final Node<T> next;

        Node(T value, Node<T> next) {
            this.value = value;
            this.next = next;
        }
    }

    private final AtomicReference<Node<T>> top = new AtomicReference<>(null);

    public void push(T value) {
        Node<T> newNode = new Node<>(value, null);
        Node<T> oldTop;
        do {
            oldTop = top.get();
            newNode.next = oldTop;
        } while (!top.compareAndSet(oldTop, newNode));
    }

    public T pop() {
        Node<T> oldTop;
        Node<T> newTop;
        do {
            oldTop = top.get();
            if (oldTop == null) {
                return null; // Stack is empty
            }
            newTop = oldTop.next;
        } while (!top.compareAndSet(oldTop, newTop));
        return oldTop.value;
    }

    public static void main(String[] args) {
        NonBlockingStack<Integer> stack = new NonBlockingStack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);

        System.out.println(stack.pop()); // 3
        System.out.println(stack.pop()); // 2
        System.out.println(stack.pop()); // 1
    }
}

2.3 实现非阻塞单例模式

使用 AtomicReference 可以实现一个简单的非阻塞单例模式。

import java.util.concurrent.atomic.AtomicReference;

public class NonBlockingSingleton {
    private static final AtomicReference<NonBlockingSingleton> instance = new AtomicReference<>();

    private NonBlockingSingleton() {
        // Private constructor to prevent instantiation
    }

    public static NonBlockingSingleton getInstance() {
        NonBlockingSingleton currentInstance = instance.get();
        if (currentInstance == null) {
            currentInstance = new NonBlockingSingleton();
            if (!instance.compareAndSet(null, currentInstance)) {
                currentInstance = instance.get();
            }
        }
        return currentInstance;
    }

    public static void main(String[] args) {
        NonBlockingSingleton singleton1 = NonBlockingSingleton.getInstance();
        NonBlockingSingleton singleton2 = NonBlockingSingleton.getInstance();
        System.out.println(singleton1 == singleton2); // true
    }
}

2.4 实现多线程安全的缓存

使用 AtomicReference 可以实现一个简单的多线程安全缓存。

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceCache<K, V> {
    private final AtomicReference<Entry<K, V>> cache = new AtomicReference<>();

    private static class Entry<K, V> {
        final K key;
        final V value;

        Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    public V get(K key) {
        Entry<K, V> entry = cache.get();
        if (entry != null && entry.key.equals(key)) {
            return entry.value;
        }
        return null;
    }

    public void put(K key, V value) {
        Entry<K, V> newEntry = new Entry<>(key, value);
        cache.set(newEntry);
    }

    public static void main(String[] args) {
        AtomicReferenceCache<String, String> cache = new AtomicReferenceCache<>();
        cache.put("key1", "value1");
        System.out.println(cache.get("key1")); // value1
    }
}

2.5 实现非阻塞队列

使用 AtomicReference 可以实现一个简单的非阻塞队列。

import java.util.concurrent.atomic.AtomicReference;

public class NonBlockingQueue<T> {
    private static class Node<T> {
        final T value;
        final AtomicReference<Node<T>> next;

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

    private final AtomicReference<Node<T>> head;
    private final AtomicReference<Node<T>> tail;

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

    public void enqueue(T value) {
        Node<T> newNode = new Node<>(value);
        Node<T> oldTail;
        Node<T> tailNext;
        while (true) {
            oldTail = tail.get();
            tailNext = oldTail.next.get();
            if (oldTail == tail.get()) {
                if (tailNext == null) {
                    if (oldTail.next.compareAndSet(null, newNode)) {
                        tail.compareAndSet(oldTail, newNode);
                        return;
                    }
                } else {
                    tail.compareAndSet(oldTail, tailNext);
                }
            }
        }
    }

    public T dequeue() {
        Node<T> oldHead;
        Node<T> oldTail;
        Node<T> headNext;
        while (true) {
            oldHead = head.get();
            oldTail = tail.get();
            headNext = oldHead.next.get();
            if (oldHead == head.get()) {
                if (oldHead == oldTail) {
                    if (headNext == null) {
                        return null; // Queue is empty
                    }
                    tail.compareAndSet(oldTail, headNext);
                } else {
                    T value = headNext.value;
                    if (head.compareAndSet(oldHead, headNext)) {
                        return value;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        NonBlockingQueue<Integer> queue = new NonBlockingQueue<>();
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        System.out.println(queue.dequeue()); // 1
        System.out.println(queue.dequeue()); // 2
        System.out.println(queue.dequeue()); // 3
    }
}

3. AtomicReference 和 synchronized 的区别是什么?

AtomicReference 和 synchronized 都是用于在多线程环境下确保操作安全的机制,但它们在实现方式、性能和适用场景上有很大的区别。

3.1 实现方式

  • AtomicReference:使用底层的 CAS(Compare-And-Swap)操作来实现原子性。CAS 是一种硬件级别的原子操作,可以确保在多线程环境下对内存的修改是安全的。
  • synchronized:使用内置的锁机制来确保同一时间只有一个线程可以执行被 synchronized 修饰的代码块或方法。

3.2 性能

  • AtomicReference:由于使用的是无锁的 CAS 操作,通常比 synchronized 更高效,尤其是在高并发环境下。它避免了线程上下文切换和阻塞问题。
  • synchronized:使用锁机制,在高并发环境下可能会导致线程阻塞和上下文切换,性能相对较低。

3.3 适用场景

  • AtomicReference:适用于简单的原子操作,如更新对象引用、计数器等。
  • synchronized:适用于更复杂的同步需求,如需要在一个代码块中进行多个操作,并且这些操作需要保持原子性。

3.4 使用 AtomicReference 实现线程安全的计数器

import java.util.concurrent.atomic.AtomicReference;

public class AtomicCounter {
    private final AtomicReference<Integer> count = new AtomicReference<>(0);

    public void increment() {
        Integer oldValue, newValue;
        do {
            oldValue = count.get();
            newValue = oldValue + 1;
        } while (!count.compareAndSet(oldValue, newValue));
    }

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

    public static void main(String[] args) throws InterruptedException {
        AtomicCounter counter = new AtomicCounter();
        Runnable task = counter::increment;

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

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("Final count: " + counter.getCount()); // Should print 1000
    }
}

3.5 使用 synchronized 实现线程安全的计数器

public class SynchronizedCounter {
    private int count = 0;

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

    public synchronized int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedCounter counter = new SynchronizedCounter();
        Runnable task = counter::increment;

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

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println("Final count: " + counter.getCount()); // Should print 1000
    }
}

4. AtomicReference 的 ABA 问题是什么?如何解决?

ABA 问题是指在多线程环境下,一个变量的值从 A 变成 B,然后又变回 A。在这种情况下,使用 CAS(Compare-And-Swap)操作可能会误认为变量的值没有变化,从而导致逻辑上的错误。

4.1 ABA 问题示例

让我们通过一个简单的示例来演示 ABA 问题:

import java.util.concurrent.atomic.AtomicReference;

public class ABADemo {
    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
            this.next = null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicReference<Node> head = new AtomicReference<>(new Node(1));
        head.get().next = new Node(2);

        Thread t1 = new Thread(() -> {
            Node oldHead = head.get();
            // Simulate some work with the old head
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            // Try to swap the head
            head.compareAndSet(oldHead, oldHead.next);
        });

        Thread t2 = new Thread(() -> {
            Node oldHead = head.get();
            // Simulate ABA problem: A -> B -> A
            Node next = oldHead.next;
            head.compareAndSet(oldHead, next); // A -> B
            head.compareAndSet(next, oldHead); // B -> A
        });

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

        t1.join();
        t2.join();

        System.out.println("Head value: " + head.get().value); // Expected: 1, Actual: 1
    }
}

在这个示例中,两个线程 t1 和 t2 试图修改 head。线程 t2 模拟了 ABA 问题,先将 head 从 A 变为 B,然后又变回 A。线程 t1 的 compareAndSet 操作可能会误认为 head 没有变化,从而导致逻辑上的错误。

4.2 解决 ABA 问题

Java 提供了 AtomicStampedReference 和 AtomicMarkableReference 来解决 ABA 问题。它们在引用的同时存储一个额外的标记或版本号,以检测是否发生了 ABA 问题。
使用 AtomicStampedReference 解决 ABA 问题:

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {
    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
            this.next = null;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Node initialNode = new Node(1);
        initialNode.next = new Node(2);
        AtomicStampedReference<Node> head = new AtomicStampedReference<>(initialNode, 0);

        Thread t1 = new Thread(() -> {
            int[] stampHolder = new int[1];
            Node oldHead = head.get(stampHolder);
            int oldStamp = stampHolder[0];
            // Simulate some work with the old head
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            // Try to swap the head
            head.compareAndSet(oldHead, oldHead.next, oldStamp, oldStamp + 1);
        });

        Thread t2 = new Thread(() -> {
            int[] stampHolder = new int[1];
            Node oldHead = head.get(stampHolder);
            int oldStamp = stampHolder[0];
            // Simulate ABA problem: A -> B -> A
            Node next = oldHead.next;
            head.compareAndSet(oldHead, next, oldStamp, oldStamp + 1); // A -> B
            head.compareAndSet(next, oldHead, oldStamp + 1, oldStamp + 2); // B -> A
        });

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

        t1.join();
        t2.join();

        int[] stampHolder = new int[1];
        System.out.println("Head value: " + head.get(stampHolder).value); // Expected: 1, Actual: 1
        System.out.println("Stamp: " + stampHolder[0]); // Should be greater than 0
    }
}

在这个示例中,AtomicStampedReference 用于存储节点的引用和一个整数标记(版本号)。每次 compareAndSet 操作不仅检查引用是否相同,还检查版本号是否匹配,从而避免了 ABA 问题。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

17.jdk源码阅读之LinkedBlockingQueue

18.jdk源码阅读之CopyOnWriteArrayList

19.jdk源码阅读之FutureTask

20.jdk源码阅读之CompletableFuture

21.jdk源码阅读之AtomicLong

22.jdk源码阅读之Thread(上)

23.jdk源码阅读之Thread(下)

24.jdk源码阅读之ExecutorService

25.jdk源码阅读之Executors

26.jdk源码阅读之ConcurrentLinkedQueue

27.jdk源码阅读之ConcurrentLinkedDeque

28.jdk源码阅读之CopyOnWriteArraySet

29.jdk源码阅读之Exchanger

30.jdk源码阅读之ReentrantReadWriteLock

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

至真源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值