Java并发 - 原子类
CAS 原子操作
CAS(Compare and Swap)是一种并发算法,通常用于实现多线程环境下的同步操作,特别是在并发编程中实现无锁算法。CAS操作涉及三个参数:内存位置(V)、期望值(A)和新值(B)。操作的意义是:仅当V的值等于A时,才将V的值更新为B。整个操作是原子的,不会被其他线程中断。
下面是CAS的基本原理:
- 读取内存值(V): 线程首先读取共享变量的当前值(V)。
- 比较并交换(Compare and Swap): 线程比较读取的值(V)与预期的值(A)。如果相等,说明在读取值的过程中没有其他线程对该变量进行修改,那么线程将新值(B)写入内存位置;否则,说明有其他线程对该变量进行了修改,
CAS操作失败,线程需要重新尝试。
- 原子性保证: 整个比较并交换的过程是原子性的,即在整个操作过程中,不会被其他线程中断。
CAS优点:
避免了使用锁带来的性能开销,因为它不会使线程阻塞,而是采用乐观的方式尝试更新共享变量
CAS缺点:
ABA问题: 如果一个变量原来的值是A,线程1将其改为B,然后又改回A,此时线程2通过CAS检查发现值仍然是A,认为没有被修改,但实际上已经发生了变化。为了解决ABA问题,可以使用版本号等方式引入更多信息。
- 循环时间长开销大: 在
CAS操作失败时,线程需要不断地重试,直到成功为止。这可能导致一些线程长时间无法完成操作,增加了开销。
- 只能保证一个共享变量的原子操作:
CAS只能对单一的共享变量进行原子操作,无法支持类似于整个事务的复合操作。
CAS 示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class CASExample { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(0); boolean casResult = atomicInteger.compareAndSet(0, 1);
if (casResult) { System.out.println("CAS success. new value: " + atomicInteger.get()); } else { System.out.println("CAS failed."); } casResult = atomicInteger.compareAndSet(0, 2);
if (casResult) { System.out.println("CAS successful. new value: " + atomicInteger.get()); } else { System.out.println("CAS failed."); } } }
|
1 2 3
| Connected to the target VM, address: '127.0.0.1:64335', transport: 'socket' CAS success. new value: 1 CAS failed.
|
UnSafe 原子操作
Unsafe 类是 Java 中的一个非常特殊且强大的类,它提供了直接访问内存和执行 CAS(Compare and Swap)等底层操作的方法。然而,Unsafe 类并不是官方公开的 API,并且在 Java 9 中进行了限制,不再推荐使用。因此,如果可能,最好避免直接使用 Unsafe 类。
以下是一些 Unsafe 类的主要功能:
- 内存操作:
Unsafe 类提供了一些方法,可以直接操作内存,如allocateMemory、freeMemory、putXXX 和 getXXX 等方法,其中 XXX 表示不同的数据类型。
- 对象操作:
Unsafe 类允许直接操作对象的内部字段,比如获取和设置字段的值,甚至可以直接修改对象的类。这些操作可能绕过了 Java 的访问权限检查。
CAS 操作: Unsafe 类提供了 CAS 相关的方法,例如 compareAndSwapInt、compareAndSwapLong、compareAndSwapObject 等,用于实现无锁算法。
- 数组操作:
Unsafe 提供了一系列用于操作数组元素的方法,例如 putIntVolatile、getIntVolatile 等。
- 类加载:
Unsafe 类还提供了一些用于加载类和定义类的方法。
AtomicInteger
AtomicInteger 是一种用于执行原子操作的整型类。它常用于在多线程环境下对计数器进行操作。
1 2 3 4 5 6 7
| int get() void set(int newValue) int getAndIncrement() int incrementAndGet() boolean compareAndSet(int expect, int update) int addAndGet(int delta) void lazySet(int newValue)
|
AtomicInteger使用示例
1 2 3 4 5 6 7 8 9 10 11
| import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerExample { public static void main(String[] args) { AtomicInteger counter = new AtomicInteger(0);
int incrementedValue = counter.incrementAndGet(); System.out.println("Incremented Value: " + incrementedValue); } }
|
AtomicBoolean
AtomicBoolean 提供对布尔类型变量的原子操作,通常用于在多线程环境下实现一些状态标记。
1 2 3 4 5 6 7 8 9 10
| boolean get()
void set(boolean newValue)
boolean getAndSet(boolean newValue)
boolean compareAndSet(boolean expect, boolean update)
boolean weakCompareAndSet(boolean expect, boolean update)
|
AtomicBoolean使用示例
1 2 3 4 5 6 7 8 9 10 11
| import java.util.concurrent.atomic.AtomicBoolean;
public class AtomicBooleanExample { public static void main(String[] args) { AtomicBoolean flag = new AtomicBoolean(true);
flag.compareAndSet(true, false); System.out.println("Flag Value: " + flag.get()); } }
|
AtomicReference
AtomicReference 允许原子性地操作引用类型变量。下面是一个示例,演示如何原子性地更新引用值
1 2 3 4 5 6 7
| AtomicReference(V initialValue) V get() void set(V newValue) boolean compareAndSet(V expect, V update) V getAndSet(V newValue) boolean weakCompareAndSet(V expect, V update) String toString()
|
AtomicReference使用示例
1 2 3 4 5 6 7 8 9 10 11 12
| import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceExample { public static void main(String[] args) { AtomicReference<String> reference = new AtomicReference<>("initialValue");
reference.compareAndSet("initialValue", "newValue");
System.out.println("Reference Value: " + reference.get()); } }
|
AtomicStampedReference
AtomicStampedReference 在 AtomicReference 的基础上增加了版本号,用于解决ABA问题。
1 2 3 4 5 6 7 8 9 10 11 12
| AtomicStampedReference(V initialRef, int initialStamp)
V getReference()
int getStamp()
V get(int[] stampHolder)
boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
boolean weakCompareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
|
AtomicStampedReference使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceExample { public static void main(String[] args) { AtomicStampedReference<String> stampedReference = new AtomicStampedReference<>("initialValue", 0); stampedReference.compareAndSet("initialValue", "newValue", 0, 1); System.out.println("Stamped Reference Value: " + stampedReference.getReference()); System.out.println("Stamped Reference Stamp: " + stampedReference.getStamp()); } }
|