前言
Java中的原子变量是用于实现无锁的线程安全编程的一种机制。它们是java.util.concurrent.atomic包中的一部分,这个包提供了一系列原子类,用于执行原子操作。
主要类型
Java的原子包提供了多种原子类,包括:
- 基本类型:如
AtomicInteger
,AtomicLong
,AtomicBoolean
,它们分别为基本数据类型int
,long
,boolean
提供原子操作。 - 数组类型:如
AtomicIntegerArray
,AtomicLongArray
,AtomicReferenceArray
,它们允许对数组的元素进行原子操作。 - 复杂类型:
AtomicReference
用于对象引用的原子操作,AtomicStampedReference
和AtomicMarkableReference
提供了带有版本号或标记位的对象引用操作,用于解决ABA问题。
使用方式
public class Counter {private AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();}public int getCount() {return count.get();}
}
在这个例子中,increment()
方法通过incrementAndGet()
原子地增加计数器的值。这意味着即使多个线程同时调用increment()
方法,每次调用也都会安全地增加计数器的值,且每个线程看到的count
值都是最新的。
原理
Java 中的原子变量之所以能够实现线程安全,主要是基于以下几个核心原理:
原子变量的实现采用了无锁编程技术,这意味着它们不依赖于传统的锁机制(如 synchronized 或 ReentrantLock)来实现线程之间的同步。无锁编程通过一种更加轻量级的方法来处理数据的并发修改,从而减少了线程阻塞和上下文切换的开销。
原子变量中的关键操作(如读取、写入、修改)是通过原子指令来实现的。这些原子指令在底层硬件(如现代的多核处理器)的支持下,保证了在执行过程中的不可中断性。最常用的原子指令包括“比较并交换”(CAS),它包括以下步骤:
- 读取原始值:首先读取当前的内存位置中的值。
- 执行比较:检查该值是否仍然等于预期的原值,如果不等于,说明其他线程已经修改了该值,操作失败。
- 条件更新:如果预期值仍然有效,那么更新为新的值。这整个过程是一个单一的、不可分割的操作。
Java 的原子变量利用了 volatile 变量的内存语义,确保了对这些变量的修改对所有其他线程都是可见的。使用 volatile 变量时,写入变量的操作会确保之前的所有操作对其他线程都是可见的,而从 volatile 变量读取则会确保看到最近一次写入的值。
由于原子变量的操作直接在硬件级别支持,因此它们的执行速度比重量级的锁快得多。这种无锁的特性尤其适合读多写少的场景,可以大幅提高并发性能。
volatile
当一个字段被声明为volatile
时,它可以确保所有线程都能看到该字段的最新值。在没有volatile
声明的情况下,线程可能从自己的线程栈中缓存变量的值,这意味着当一个线程更新了这个变量时,变化可能对其他线程不可见。
Java内存模型允许编译器和处理器对操作顺序进行重排序,以优化性能。然而,这种重排序不应当干扰单线程程序的执行逻辑。在多线程环境中,为了维护一致性,重排序可能会导致严重问题。当变量被声明为volatile
后,任何对该变量的读写操作都将作为内存屏障,防止这类操作与其他内存操作进行重排序。任何写操作之后都会插入一个写屏障,任何读操作之前都会插入一个读屏障。
- 写屏障:确保在该屏障之前的所有写操作(不仅仅是对
volatile
变量的写操作)都在写入volatile
变量后对其他线程可见。 - 读屏障:确保在该屏障之后的所有读操作能看到该
volatile
变量之前的写入。
单个读或写操作对于volatile
变量是原子的,某些由多个步骤组成的操作却不能保证在没有外部同步的情况下原子性。
CAS操作实现自旋锁
自旋锁与互斥锁不同,自旋锁的原理是不断尝试占有这把锁,失败就会等待下次再次尝试,互斥锁则是当其他线程尝试占有此锁时将把该线程堵塞,直至锁被释放唤醒
又因为线程被阻塞和唤醒资源消耗较大,
所以自旋锁适用于锁占用很少时间的情况,互斥锁使用于占有锁时间较长的情况。
import java.util.concurrent.atomic.AtomicBoolean;public class SpinLock {private final AtomicBoolean lock = new AtomicBoolean(false);public void lock() {while (!lock.compareAndSet(false, true)) {// 循环直到成功将lock从false设置为true}}public void unlock() {lock.set(false);}
}