问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深入解析 JUC 包中的 Atomic 原子类:高并发编程的利器

创作时间:
作者:
@小白创作中心

深入解析 JUC 包中的 Atomic 原子类:高并发编程的利器

引用
CSDN
1.
https://m.blog.csdn.net/suyuaidan/article/details/145835036

在当今高并发的互联网时代,如何高效地处理多线程问题成为了每个开发者必须面对的挑战。Java的java.util.concurrent.atomic包为我们提供了一系列强大的工具——Atomic原子类,它们通过无锁的CAS操作,帮助我们在多线程环境下实现高效的线程安全操作。无论是基本类型的原子操作,还是解决ABA问题的版本控制,Atomic类都展现出了其独特的优势。

本文将带你深入解析JUC包中的Atomic原子类,从CAS操作的原理到各类Atomic的使用场景,再到面试中常见的问题解析,帮助你全面掌握这些高并发编程的利器。无论你是正在准备面试,还是希望提升自己的并发编程能力,这篇文章都将为你提供实用的知识和技巧。

让我们一起探索Atomic类的奥秘,解锁高并发编程的新境界!🚀

基本类型原子类

这些类用于对基本数据类型进行原子操作。

  • AtomicInteger

用于对int类型进行原子操作。常用方法包括:

  • incrementAndGet():原子地将当前值加1并返回新值。

  • decrementAndGet():原子地将当前值减1并返回新值。

  • getAndSet(int newValue):原子地设置新值并返回旧值。

  • compareAndSet(int expect, int update):如果当前值等于期望值,则原子地设置为新值。

  • AtomicLong

用于对long类型进行原子操作。方法与AtomicInteger类似。

  • AtomicBoolean

用于对boolean类型进行原子操作。常用方法包括:

  • compareAndSet(boolean expect, boolean update):如果当前值等于期望值,则原子地设置为新值。

数组类型原子类

这些类用于对数组中的元素进行原子操作。

  • AtomicIntegerArray

用于对int数组中的元素进行原子操作。常用方法包括:

  • getAndSet(int i, int newValue):原子地设置数组第i个元素的值并返回旧值。

  • compareAndSet(int i, int expect, int update):如果数组第i个元素等于期望值,则原子地设置为新值。

  • AtomicLongArray

用于对long数组中的元素进行原子操作。方法与AtomicIntegerArray类似。

  • AtomicReferenceArray

用于对引用类型数组中的元素进行原子操作。常用方法包括:

  • getAndSet(int i, E newValue):原子地设置数组第i个元素的值并返回旧值。
  • compareAndSet(int i, E expect, E update):如果数组第i个元素等于期望值,则原子地设置为新值。

引用类型原子类

这些类用于对引用类型进行原子操作。

  • AtomicReference

用于对引用类型进行原子操作。常用方法包括:

  • getAndSet(E newValue):原子地设置新值并返回旧值。

  • compareAndSet(E expect, E update):如果当前值等于期望值,则原子地设置为新值。

  • AtomicStampedReference

AtomicReference的基础上增加了一个int类型的版本号(stamp),用于解决ABA问题。常用方法包括:

  • compareAndSet(E expectedReference, E newReference, int expectedStamp, int newStamp):如果当前引用和版本号都等于期望值,则原子地设置为新引用和新版本号。

  • AtomicMarkableReference

AtomicReference的基础上增加了一个boolean类型的标记(mark),用于解决ABA问题。常用方法包括:

  • compareAndSet(E expectedReference, E newReference, boolean expectedMark, boolean newMark):如果当前引用和标记都等于期望值,则原子地设置为新引用和新标记。

字段更新器

这些类用于对对象的字段进行原子操作。

  • AtomicIntegerFieldUpdater

用于对对象的int类型字段进行原子操作。常用方法包括:

  • compareAndSet(T obj, int expect, int update):如果对象的字段值等于期望值,则原子地设置为新值。

  • AtomicLongFieldUpdater

用于对对象的long类型字段进行原子操作。方法与AtomicIntegerFieldUpdater类似。

  • AtomicReferenceFieldUpdater

用于对对象的引用类型字段进行原子操作。常用方法包括:

  • compareAndSet(T obj, E expect, E update):如果对象的字段值等于期望值,则原子地设置为新值。

其他原子类

  • DoubleAccumulatorLongAccumulator

用于对doublelong类型进行累加操作,支持自定义累加函数。

  • DoubleAdderLongAdder

用于对doublelong类型进行高效的累加操作,适用于高并发场景。

面试问题及解析

  1. 什么是CAS操作?它在Atomic类中是如何应用的?

问题解析:

CAS(Compare-And-Swap)是一种无锁的原子操作,用于在多线程环境下实现线程安全。CAS操作包含三个操作数:内存位置(V)、预期值(A)和新值(B)。如果内存位置的值等于预期值,则将内存位置的值更新为新值;否则,不做任何操作。

在Atomic类中,CAS操作是通过底层的硬件指令实现的,例如compareAndSet()方法。这个方法会检查当前值是否等于预期值,如果相等则更新为新值,否则不做任何操作。

示例代码:

AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.compareAndSet(0, 1); // 如果当前值是0,则更新为1
  1. AtomicInteger和synchronized关键字有什么区别?

问题解析:

  • AtomicInteger使用CAS操作实现线程安全,适用于高并发场景,避免了锁的开销。
  • synchronized关键字通过加锁实现线程安全,适用于需要复杂同步控制的场景,但会带来性能开销。

示例代码:

// 使用AtomicInteger
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();

// 使用synchronized
int counter = 0;
synchronized (this) {
    counter++;
}
  1. 什么是ABA问题?AtomicStampedReference是如何解决这个问题的?

问题解析:

ABA问题是指在CAS操作中,一个变量的值从A变为B,然后又变回A,CAS操作会误认为这个变量没有被修改过。

AtomicStampedReference通过引入一个版本号(stamp)来解决ABA问题。每次更新值时,版本号也会更新,CAS操作会同时检查值和版本号。

示例代码:

AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference<>(100, 0);
int[] stampHolder = new int[1];
int expectedStamp = atomicStampedRef.getStamp();
atomicStampedRef.compareAndSet(100, 101, expectedStamp, expectedStamp + 1);
  1. AtomicIntegerArray和普通的int数组有什么区别?

问题解析:

  • AtomicIntegerArray提供了对数组元素的原子操作,适用于多线程环境。
  • 普通的int数组在多线程环境下需要额外的同步控制,例如使用synchronizedReentrantLock

示例代码:

// 使用AtomicIntegerArray
AtomicIntegerArray atomicArray = new AtomicIntegerArray(10);
atomicArray.getAndSet(0, 1);

// 使用普通int数组
int[] array = new int[10];
synchronized (this) {
    array[0] = 1;
}
  1. AtomicReference和AtomicStampedReference有什么区别?

问题解析:

  • AtomicReference用于对引用类型进行原子操作,但不解决ABA问题。
  • AtomicStampedReference在AtomicReference的基础上增加了版本号(stamp),用于解决ABA问题。

示例代码:

// 使用AtomicReference
AtomicReference<String> atomicRef = new AtomicReference<>("A");
atomicRef.compareAndSet("A", "B");

// 使用AtomicStampedReference
AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>("A", 0);
int[] stampHolder = new int[1];
int expectedStamp = atomicStampedRef.getStamp();
atomicStampedRef.compareAndSet("A", "B", expectedStamp, expectedStamp + 1);
  1. AtomicLong和LongAdder有什么区别?

问题解析:

  • AtomicLong使用CAS操作实现线程安全,适用于低并发场景。
  • LongAdder使用分段锁(striped locking)实现线程安全,适用于高并发场景,性能优于AtomicLong。

示例代码:

// 使用AtomicLong
AtomicLong atomicLong = new AtomicLong(0);
atomicLong.incrementAndGet();

// 使用LongAdder
LongAdder longAdder = new LongAdder();
longAdder.increment();
  1. AtomicIntegerFieldUpdater是如何工作的?

问题解析:

AtomicIntegerFieldUpdater用于对对象的int类型字段进行原子操作。它通过反射机制访问对象的字段,并使用CAS操作保证线程安全。

示例代码:

class MyClass {
    volatile int value;
}
AtomicIntegerFieldUpdater<MyClass> updater = AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "value");
MyClass obj = new MyClass();
updater.compareAndSet(obj, 0, 1);

总结

JUC包中的Atomic原子类提供了一种高效的无锁线程安全操作方式,适用于高并发场景。通过使用CAS操作,这些类能够在多线程环境下保证操作的原子性,避免了使用锁带来的性能开销。在实际开发中,根据具体需求选择合适的原子类,可以显著提升并发程序的性能和可靠性。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号