在Java中,原子操作由java.util.concurrent.atomic
包中的一系列类提供,这些类利用了底层的无锁编程技术(通常是比较并交换,CAS操作),确保了单个变量在多线程环境中的线程安全性。
原子类,如AtomicInteger
、AtomicLong
、AtomicBoolean
等,都提供了一种机制,允许在多线程环境中无锁地、线程安全地操作单个变量。
CAS(Compare-And-Swap)是实现原子性的核心操作,它是一个原子指令,用于管理对共享数据的并发访问。CAS操作包含三个操作数 —— 内存位置、预期原值及新值。执行时,CAS只有在内存位置的当前值等于预期原值时,才会将内存位置的内容更新为新值,这整个过程是原子的。
下面是一个简单的使用AtomicInteger
的Java代码示例:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private AtomicInteger count = new AtomicInteger(0);
// 原子地将当前值增加1
public void increment() {
count.incrementAndGet();
}
// 原子地将当前值减少1
public void decrement() {
count.decrementAndGet();
}
// 获取当前值
public int getCount() {
return count.get();
}
public static void main(String[] args) {
AtomicExample example = new AtomicExample();
// 创建两个线程,一个增加计数,另一个减少计数
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.decrement();
}
});
// 启动线程
t1.start();
t2.start();
// 等待两个线程执行完毕
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终的计数值,理论上应该为0
System.out.println("Final count: " + example.getCount());
}
}
在这个例子中,我们创建了一个包含AtomicInteger
的类AtomicExample
。这个类的increment()
和decrement()
方法分别使用原子操作来增加和减少计数器的值。然后在main
方法中,我们创建了两个线程来同时增加和减少计数器的值,由于AtomicInteger
的操作是原子的,即使在多线程环境中,它也能保证计数器的正确性。
atomic 的原理
Atomic包中的类基本的特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量的值进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,一直等到执行成功。
Atomic系列的类中的核心方法都会调用unsafe类中的几个本地方法。我们需要先知道一个东西就是Unsafe类,全名为:sun.misc.Unsafe,这个类包含了大量的对C代码的操作,包括很多直接内存分配以及原子操作的调用,而它之所以标记为非安全的,是告诉你这个里面大量的方法调用都会存在安全隐患,需要小心使用,否则会导致严重的后果