【Java原理系列】 AtomicInteger原理用法源码详解
Java AtomicInteger原理用法源码详解
基本原理
Java 中的 AtomicInteger
是一个原子类,用于实现对整数类型的原子操作。它提供了一系列方法,如 get()
、set()
、incrementAndGet()
、compareAndSet()
等,可以在多线程环境下安全地进行原子操作。通过使用 AtomicInteger
,可以避免使用显式的锁来保护共享整数变量,从而提高并发性能。它适用于许多并发场景,如计数器、标记位等。
AtomicInteger
的原理基于 CAS(Compare-And-Swap)操作。它内部使用了 Unsafe
类或者其他底层的原子操作机制来实现线程安全的操作。通过 CAS 操作,AtomicInteger
可以保证在多线程环境下对整数值的修改是原子性的,避免了竞态条件和数据不一致等问题。
AtomicInteger
的主要特点和原理如下:
- 内部使用
volatile
修饰的 int 变量:AtomicInteger
内部有一个被volatile
修饰的 int 变量,用于存储整数值。volatile
关键字确保了变量的可见性,使得对该变量的读写操作具有原子性。 - 使用 CAS 操作进行原子更新:
AtomicInteger
中的原子操作方法都是基于 CAS 操作实现的。CAS 操作包括比较内存中的值与预期值是否相等,如果相等则更新为新值,否则操作失败。CAS 操作利用底层硬件提供的原子指令,确保操作的原子性。 - 循环重试:由于 CAS 操作可能在多线程环境下失败,因此
AtomicInteger
在实现中使用了循环重试的机制。如果 CAS 操作失败,它会不断尝试进行 CAS 操作,直到成功为止。
注意事项
-
AtomicInteger
并不能完全替代Integer
类,因为它并没有提供自动装箱和拆箱的功能。 -
虽然
AtomicInteger
提供了原子操作,但仍然需要根据具体情况考虑原子类的使用范围和正确性。有时候,可能需要额外的同步手段来保证一些复合操作的原子性。假设需要对一个计数器进行原子递增和条件检查,以决定是否执行某个操作。以下是一个示例代码:
if (counter.incrementAndGet() >= threshold) {
// 执行操作
}
尽管递增操作和条件检查各自都是原子的,但由于两个操作之间存在竞态条件,可能会导致结果不一致。因为在判断条件后,其他线程可能已经对计数器进行了修改。在这种情况下,可以使用额外的同步手段,如锁(`synchronized`)或者显示的使用 `Lock` 接口,来保证多个操作的原子性,可以确保递增操作、条件检查和操作的执行之间是原子的,避免了竞态条件。
```java
synchronized (lock) {
if (counter.incrementAndGet() >= threshold) {
// 执行操作
}
}
基本方法
以下是对 AtomicInteger
类中提供的方法进行总结:
-
AtomicInteger(int initialValue)
:使用给定的初始值创建一个新的AtomicInteger
对象。 -
AtomicInteger()
:创建一个新的AtomicInteger
对象,并将初始值设为 0。 -
int get()
:获取当前存储的整数值。 -
void set(int newValue)
:设置整数值为指定的新值。 -
void lazySet(int newValue)
:最终将整数值设置为指定的新值。不保证立即生效,可能会延迟。 -
int getAndSet(int newValue)
:将整数值设置为指定的新值,并返回先前的值。 -
boolean compareAndSet(int expect, int update)
:如果当前值等于预期值,则原子地将整数值设置为指定的新值,并返回操作是否成功。 -
boolean weakCompareAndSet(int expect, int update)
:与compareAndSet()
方法类似,但不提供强有力的保证和排序保证。 -
int getAndIncrement()
:先返回当前值,然后将整数值增加 1。 -
int getAndDecrement()
:先返回当前值,然后将整数值减少 1。 -
int getAndAdd(int delta)
:先返回当前值,然后将整数值增加指定的增量。 -
int incrementAndGet()
:将整数值增加 1,然后返回更新后的值。 -
int decrementAndGet()
:将整数值减少 1,然后返回更新后的值。 -
int addAndGet(int delta)
:将整数值增加指定的增量,然后返回更新后的值。 -
int getAndUpdate(IntUnaryOperator updateFunction)
:先获取当前值,然后将整数值更新为通过应用给定函数得到的新值,返回先前的值。 -
int updateAndGet(IntUnaryOperator updateFunction)
:先获取当前值,然后将整数值更新为通过应用给定函数得到的新值,返回更新后的值。 -
int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)
:先获取当前值,然后将整数值与给定值通过应用给定函数进行累积计算,返回先前的值。 -
int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)
:先获取当前值,然后将整数值与给定值通过应用给定函数进行累积计算,返回更新后的值。 -
String toString()
:以字符串形式返回当前存储的整数值。 -
int intValue()
:将当前存储的整数值转换为int
类型并返回。 -
long longValue()
:将当前存储的整数值转换为long
类型并返回。 -
float floatValue()
:将当前存储的整数值转换为float
类型并返回。 -
double doubleValue()
:将当前存储的整数值转换为double
类型并返回。
这些方法允许原子地对整数值进行各种操作,确保在多线程环境下的线程安全性。
场景示例
以下是几个适用场景和示例:
- 计数器(Counter):
AtomicInteger
可以用作计数器,在多线程环境下实现原子的自增、自减操作。例如,统计网站访问次数或任务完成数量等。
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
public void decrement() {
count.decrementAndGet();
}
public int getCount() {
return count.get();
}
}
- 并发任务处理:在并发任务处理中,可以使用
AtomicInteger
来控制任务的执行顺序或限制同时执行的任务数量。
import java.util.concurrent.atomic.AtomicInteger;
public class TaskProcessor {
private static final int MAX_CONCURRENT_TASKS = 10;
private AtomicInteger activeTasks = new AtomicInteger(0);
public void processTask(Runnable task) {
// 检查是否达到最大并发任务数
while (activeTasks.get() >= MAX_CONCURRENT_TASKS) {
// 等待其他任务完成后再继续执行
Thread.yield();
}
try {
activeTasks.incrementAndGet(); // 增加活动任务数
task.run(); // 执行任务
} finally {
activeTasks.decrementAndGet(); // 减少活动任务数
}
}
}
- 线程安全的共享变量:当多个线程需要共享某个变量时,可以使用
AtomicInteger
来保证线程安全。例如,在多个线程中对共享计数器进行递增操作。
import java.util.concurrent.atomic.AtomicInteger;
public class SharedVariableExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
counter.incrementAndGet(); // 原子递增操作
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Counter value: " + counter.get());
}
}
在上述示例中,两个线程并发地对 counter
进行递增操作,通过 AtomicInteger
来保证递增操作的原子性,并最终输出正确的计数器值。
总之,AtomicInteger
适用于多线程环境下对整数进行原子操作的场景,如计数器、并发任务处理和线程安全的共享变量等。它提供了一种高效且线程安全的方式来处理整数操作。
中文源码
/**
* 一个可以原子更新的 {@code int} 值。请参阅 {@link java.util.concurrent.atomic} 包规范,了解原子变量的属性描述。
* {@code AtomicInteger} 在诸如原子递增计数器等应用中使用,不能用作 {@link java.lang.Integer} 的替代品。
* 但是,该类扩展了 {@code Number} 类,以便允许工具和实用程序对基于数字的类进行统一访问。
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 设置使用 Unsafe.compareAndSwapInt 进行更新
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex);
}
}
private volatile int value; // 使用 volatile 修饰,保证可见性
/**
* 使用给定的初始值创建一个新的 {@code AtomicInteger}。
*
* @param initialValue 初始值
*/
public AtomicInteger(int initialValue) {
value = initialValue; // 初始化整数值
}
/**
* 创建一个初始值为 {@code 0} 的新 {@code AtomicInteger}。
*/
public AtomicInteger() {
}
/**
* 获取当前值。
*
* @return 当前值
*/
public final int get() {
return value; // 获取当前存储的整数值
}
/**
* 设置为给定值。
*
* @param newValue 新值
*/
public final void set(int newValue) {
value = newValue; // 设置整数值为指定的新值
}
/**
* 最终设置为给定值。
*
* @param newValue 新值
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue); // 最终设置整数值为指定的新值,可能会延迟生效
}
/**
* 原子地设置为给定值,并返回旧值。
*
* @param newValue 新值
* @return 先前的值
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue); // 原子地设置整数值为指定的新值,并返回先前的值
}
/**
* 如果当前值 {@code ==} 预期值,则原子地将值设置为给定更新值。
*
* @param expect 预期值
* @param update 新值
* @return {@code true} 表示成功。返回 {@code false} 表示实际值不等于预期值。
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update); // 如果当前值等于预期值,则原子地将整数值设置为指定的新值,并返回操作是否成功
}
/**
* 如果当前值 {@code ==} 预期值,则原子地将值设置为给定更新值。
*
* <p><a href="package-summary.html#weakCompareAndSet">可能会出现虚假失败,不提供排序保证</a>,
* 因此很少作为 {@code compareAndSet} 的替代方法。
*
* @param expect 预期值
* @param update 新值
* @return {@code true} 表示成功
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update); // 弱 CAS 操作,不提供强有力的保证和排序保证
}
/**
* 原子地将当前值加 1。
*
* @return 先前的值
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1); // 先返回当前值,然后将整数值增加 1
}
/**
* 原子地将当前值减 1。
*
* @return 先前的值
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1); // 先返回当前值,然后将整数值减少 1
}
/**
* 原子地将给定值加到当前值。
*
* @param delta 要添加的值
* @return 先前的值
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta); // 先返回当前值,然后将整数值增加指定的增量
}
/**
* 原子地将当前值加 1。
*
* @return 更新后的值
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1; // 将整数值增加 1,然后返回更新后的值
}
/**
* 原子地将当前值减 1。
*
* @return 更新后的值
*/
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1; // 将整数值减少 1,然后返回更新后的值
}
/**
* 原子地将给定值添加到当前值。
*
* @param delta 要添加的值
* @return 更新后的值
*/
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta; // 将整数值增加指定的增量,然后返回更新后的值
}
/**
* 使用给定函数原子地更新当前值,并返回先前的值。由于可能在线程之间产生争用时重新应用尝试更新,
* 因此该函数应是无副作用的。
*
* @param updateFunction 无副作用的函数
* @return 先前的值
* @since 1.8
*/
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return prev;
}
/**
* 使用给定函数原子地更新当前值,并返回更新后的值。由于可能在线程之间产生争用时重新应用尝试更新,
* 因此该函数应是无副作用的。
*
* @param updateFunction 无副作用的函数
* @return 更新后的值
* @since 1.8
*/
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
}
/**
* 使用给定函数将当前值和给定值原子地更新,并返回先前的值。由于可能在线程之间产生争用时重新应用尝试更新,
* 因此该函数应是无副作用的。该函数将当前值作为第一个参数,给定更新作为第二个参数应用。
*
* @param x 更新值
* @param accumulatorFunction 两个参数的无副作用函数
* @return 先前的值
* @since 1.8
*/
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
/**
* 使用给定函数将当前值和给定值原子地更新,并返回更新后的值。由于可能在线程之间产生争用时重新应用尝试更新,
* 因此该函数应是无副作用的。该函数将当前值作为第一个参数,给定更新作为第二个参数应用。
*
* @param x 更新值
* @param accumulatorFunction 两个参数的无副作用函数
* @return 更新后的值
* @since 1.8
*/
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return next;
}
/**
* 返回当前值的字符串表示形式。
*
* @return 当前值的字符串表示形式
*/
public String toString() {
return Integer.toString(get());
}
/**
* 将此 {@code AtomicInteger} 的值作为 {@code int} 返回。
*/
public int intValue() {
return get();
}
/**
* 将此 {@code AtomicInteger} 的值作为 {@code long} 返回,进行扩展原始转换。
* @jls 5.1.2 扩展原始类型转换
*/
public long longValue() {
return (long)get();
}
/**
* 将此 {@code AtomicInteger} 的值作为 {@code float} 返回,进行扩展原始转换。
* @jls 5.1.2 扩展原始类型转换
*/
public float floatValue() {
return (float)get();
}
/**
* 将此 {@code AtomicInteger} 的值作为 {@code double} 返回,进行扩展原始转换。
* @jls 5.1.2 扩展原始类型转换
*/
public double doubleValue() {
return (double)get();
}
}
官方链接
更多推荐
所有评论(0)