包介绍
JDK的atomic包提供了一组原子类,用于多线程环境中的原子操作,确保线程安全和高性能。
Atomic包是Java在并发编程中的重要工具,它利用CAS(Compare-And-Swap)机制保证操作的原子性,同时避免了重量级锁的使用,提高了并发性能。Atomic包中的类通过底层处理器提供的原子指令实现,但不同的CPU架构可能提供的原子指令不同,有时可能需要某种形式的内部锁,因此不能绝对保证线程不被阻塞。
基本类型
AtomicInteger
提供了一种线程安全的方式来操作整数。
主要提供了以下方法:
getAndIncrement()
: 获取当前值并自增,返回旧值。get()
: 获取当前值。compareAndSet(int expect, int update)
: 如果当前值等于预期值(expect),则将其更新为新的值(update)。如果更新成功,返回 true;否则返回 false。getAndSet(int newValue)
: 获取当前值并设置为指定的新值,返回旧值。floatValue()
: 将当前值转换为 float 类型。getAndAdd(int delta)
: 获取当前值并加上指定的增量(delta),返回旧值。getAndUpdate(IntUnaryOperator updateFunction)
: 获取当前值并应用给定的一元操作函数(updateFunction),返回旧值。intValue()
: 将当前值转换为 int 类型。getAndAccumulate(int x, IntBinaryOperator accumulatorFunction)
: 获取当前值并与给定的值(x)应用二元操作函数(accumulatorFunction),返回旧值。toString()
: 返回当前值的字符串表示形式。lazySet(int newValue)
: 设置当前值为指定的新值,但不保证立即可见性,可能会延迟到下一次访问。incrementAndGet()
: 自增并返回新值。accumulateAndGet(int x, IntBinaryOperator accumulatorFunction)
: 与给定的值(x)应用二元操作函数(accumulatorFunction)并返回结果。decrementAndGet()
: 自减并返回新值。longValue()
: 将当前值转换为 long 类型。addAndGet(int delta)
: 增加指定的增量(delta)并返回新值。doubleValue()
: 将当前值转换为 double 类型。weakCompareAndSet(int expect, int update)
: 类似于 compareAndSet,但如果当前值不等于预期值,它可能不会立即失败,而是尝试再次进行比较和设置。这在某些情况下可以提高性能,但可能导致不一致的结果。updateAndGet(IntUnaryOperator updateFunction)
: 应用给定的一元操作函数(updateFunction)并返回结果。set(int newValue)
: 设置当前值为指定的新值。
原理
-
原子性操作:AtomicInteger使用CAS(比较替换)算法来实现其内部的值的原子性更新。CAS算法是硬件级别的原子操作,它可以在没有锁的情况下进行线程安全的更新。
-
Unsafe类:AtomicInteger的内部实现使用了Unsafe类的native方法,如
compareAndSwapInt
,这是一个底层的、高效的原子操作方法,它直接与操作系统和硬件交互,实现了原子性的保证。
/**
通过调用Unsafe.getUnsafe()方法获取Unsafe实例。Unsafe类提供了一些底层操作,可以直接访问对象和内存,绕过了Java的安全检查机制。
定义了一个静态常量valueOffset,用于存储字段"value"在内存中的偏移量。
在静态初始化块中,使用unsafe.objectFieldOffset方法获取字段"value"的偏移量。这个方法需要一个Field对象作为参数,因此需要先通过反射获取AtomicInteger类的"value"字段。
如果获取偏移量的过程中发生异常(例如找不到字段),则抛出一个Error。
**/
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); }
}
- 字段更新:AtomicInteger使用volatile关键字声明其内部值字段,确保字段的可见性和有序性,防止指令重排序导致的问题。
private volatile int value;
- 方法实现:AtomicInteger提供了一系列的方法,如
getAndIncrement
、compareAndSet
等,这些方法都是利用CAS算法来实现的,它们可以在没有锁的情况下,保证操作的原子性。
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
-
性能优势:由于AtomicInteger不需要使用锁,它通常比使用
synchronized
关键字或者显式锁(如ReentrantLock)的性能要好,尤其是在高并发的情况下。 -
内存开销:AtomicInteger相对于普通的Integer会有稍微大一点的内存开销,因为它需要额外的内存来存储一些操作所需的元数据。
-
限制:尽管AtomicInteger提供了原子性的保证,但是它不能保证操作的“逻辑原子性”。例如,如果你需要检查值,然后基于这个值进行更新(比如计数器小于10就增加),这样的操作序列就不能保证原子性,因为这个过程涉及到多个步骤,而CAS只能保证单个操作的原子性。
-
可扩展性:AtomicInteger是可扩展的,意味着你可以用它作为更复杂原子操作的基础,通过组合和扩展AtomicInteger的功能,可以构建更复杂的原子操作。
-
线程兼容性:AtomicInteger的操作是线程兼容的,这意味着在多线程环境中,你可以安全地使用AtomicInteger而不用担心线程安全问题。
-
使用场景:AtomicInteger适用于那些需要线程安全的整数操作,但不需要使用锁或者不希望有锁带来的性能开销的场景。
AtomicBoolean
类提供了一种线程安全的方式来操作布尔值。
主要提供了以下方法:
-
getAndSet(boolean newValue)
: 将当前值设置为指定的新值,并返回旧值。这是一个原子操作,确保在多线程环境下的一致性。 -
set(boolean newValue)
: 将当前值设置为指定的新值。这也是一个原子操作。 -
get()
: 获取当前的布尔值。这个方法是非原子的,但在多线程环境中使用时,由于其他原子操作的存在,可以保证读取到的值是一致的。 -
compareAndSet(boolean expect, boolean update)
: 如果当前值等于预期值(expect),则将其更新为新的值(update)。如果更新成功,返回 true;否则返回 false。这是一个典型的CAS(Compare-and-Swap)操作,通常用于无锁编程。 -
lazySet(boolean newValue)
: 将当前值设置为指定的新值,但可能会延迟该操作直到下一次访问。这是一种优化手段,可以减少不必要的内存屏障开销。 -
weakCompareAndSet(boolean expect, boolean update)
: 类似于compareAndSet
,但如果当前值不等于预期值,它可能不会立即失败,而是尝试再次进行比较和设置。这在某些情况下可以提高性能,但可能导致不一致的结果。 -
toString()
: 返回当前布尔值的字符串表示形式。这个方法是非原子的,但在多线程环境中使用时,由于其他原子操作的存在,可以保证读取到的值是一致的。
AtomicLong
类提供了一种线程安全的方式来操作长整数。
主要提供了以下方法:
VMSupportsCS8()
: 返回一个布尔值,表示虚拟机是否支持CS8(Compare and Swap)操作。longValue()
: 获取当前值作为长整型(long)。intValue()
: 获取当前值作为整型(int)。toString()
: 返回当前值的字符串表示形式。incrementAndGet()
: 自增并返回新值。getAndAccumulate(long x, LongBinaryOperator accumulatorFunction)
: 获取当前值并与给定的值(x)应用二元操作函数(accumulatorFunction),返回旧值。getAndSet(long newValue)
: 获取当前值并设置为指定的新值,返回旧值。doubleValue()
: 将当前值转换为双精度浮点数(double)。set(long newValue)
: 设置当前值为指定的新值。weakCompareAndSet(long expect, long update)
: 如果当前值等于预期值(expect),则将其更新为新的值(update)。如果更新成功,返回 true;否则返回 false。getAndUpdate(LongUnaryOperator updateFunction)
: 获取当前值并应用给定的一元操作函数(updateFunction),返回旧值。getAndIncrement()
: 获取当前值并自增,返回旧值。accumulateAndGet(long x, LongBinaryOperator accumulatorFunction)
: 与给定的值(x)应用二元操作函数(accumulatorFunction)并返回结果。getAndAdd(long delta)
: 获取当前值并加上指定的增量(delta),返回旧值。get()
: 获取当前值。lazySet(long newValue)
: 设置当前值为指定的新值,但不保证立即可见性,可能会延迟到下一次访问。compareAndSet(long expect, long update)
: 如果当前值等于预期值(expect),则将其更新为新的值(update)。如果更新成功,返回 true;否则返回 false。updateAndGet(LongUnaryOperator updateFunction)
: 应用给定的一元操作函数(updateFunction)并返回结果。floatValue()
: 将当前值转换为单精度浮点数(float)。addAndGet(long delta)
: 增加指定的增量(delta)并返回新值。getAndDecrement()
: 获取当前值并自减,返回旧值。decrementAndGet()
: 自减并返回新值。
数组操作
AtomicIntegerArray
与AtomicLongArray类似,但是它用于操作int类型的数组。它也提供了一系列的原子操作方法,如get、set、compareAndSet等,以确保在多线程环境下对数组元素的操作是原子性的。
提供的以下方法:
getAndIncrement(int)
: 获取指定索引位置的当前值,并将其自增1。返回自增前的值。accumulateAndGet(int, int, IntBinaryOperator)
: 对指定索引位置的元素进行累加操作,并返回累加后的结果。累加操作由提供的IntBinaryOperator
参数定义。getAndDecrement(int)
: 获取指定索引位置的当前值,并将其自减1。返回自减前的值。getAndAccumulate(int, int, IntBinaryOperator)
: 对指定索引位置的元素进行累加操作,并返回累加后的结果。累加操作由提供的IntBinaryOperator
参数定义。compareAndSet(int, int, int)
: 如果指定索引位置的当前值等于预期值(第二个参数),则将其更新为新值(第三个参数)。返回一个布尔值表示是否成功更新。compareAndSetRaw(long, int, int)
: 类似于compareAndSet
,但使用长整型索引。addAndGet(int, int)
: 将指定的增量(第二个参数)添加到指定索引位置的元素上,并返回结果。checkedByteOffset(int)
: 检查给定的偏移量是否有效,并返回相应的字节偏移量。getAndSet(int, int)
: 获取指定索引位置的当前值,并将其设置为新值(第二个参数)。返回旧值。byteOffset(int)
: 返回指定索引位置的字节偏移量。length()
: 返回数组的长度。get(int)
: 获取指定索引位置的值。lazySet(int, int)
: 设置指定索引位置的值,但不保证立即可见性。decrementAndGet(int)
: 获取指定索引位置的当前值,并将其自减1。返回自减前的值。weakCompareAndSet(int, int, int)
: 如果指定索引位置的当前值等于预期值(第二个参数),则尝试将其更新为新值(第三个参数)。返回一个布尔值表示是否成功更新。toString()
: 返回数组的字符串表示形式。set(int, int)
: 设置指定索引位置的值。getRaw(long)
: 获取指定索引位置的值,使用长整型索引。getAndUpdate(int, IntUnaryOperator)
: 获取指定索引位置的当前值,并应用给定的一元操作函数(第二个参数)。返回操作后的结果。incrementAndGet(int)
: 获取指定索引位置的当前值,并将其自增1。返回自增后的值。getAndAdd(int, int)
: 获取指定索引位置的当前值,并将其加上指定的增量(第二个参数)。返回结果。updateAndGet(int, IntUnaryOperator)
: 应用给定的一元操作函数(第二个参数)到指定索引位置的元素上,并返回操作后的结果。
AtomicLongArray
提供了一种线程安全的方式来操作long类型的数组。它提供了一系列的原子操作方法,如get、set、compareAndSet等,这些方法可以确保在多线程环境下对数组元素的操作是原子性的。
类方法说明:
set(int index, long newValue)
: 将指定索引位置的元素设置为新值。addAndGet(int index, long delta)
: 将指定的增量(delta)添加到指定索引位置的元素上,并返回结果。getAndUpdate(int index, LongUnaryOperator updateFunction)
: 获取指定索引位置的当前值,应用给定的一元操作函数(updateFunction),并将结果写回该位置,最后返回操作前的值。getAndDecrement(int index)
: 获取指定索引位置的当前值,将其自减1,并将结果写回该位置,最后返回操作前的值。checkedByteOffset(int index)
: 检查给定的索引是否有效,并返回相应的字节偏移量。getRaw(long offset)
: 获取指定偏移量处的元素值。lazySet(int index, long newValue)
: 设置指定索引位置的元素值为新值,但不保证立即可见性。length()
: 返回数组的长度。compareAndSet(int index, long expect, long update)
: 如果指定索引位置的当前值等于预期值(expect),则将其更新为新值(update)。返回一个布尔值表示是否成功更新。accumulateAndGet(int index, long x, LongBinaryOperator accumulatorFunction)
: 对指定索引位置的元素进行累加操作,并返回累加后的结果。累加操作由提供的累加器函数(accumulatorFunction)定义。getAndIncrement(int index)
: 获取指定索引位置的当前值,并将其自增1,然后将结果写回该位置,最后返回操作前的值。getAndAccumulate(int index, long x, LongBinaryOperator accumulatorFunction)
: 对指定索引位置的元素进行累加操作,并返回累加后的结果。累加操作由提供的累加器函数(accumulatorFunction)定义。getAndAdd(int index, long delta)
: 获取指定索引位置的当前值,将其加上指定的增量(delta),然后将结果写回该位置,最后返回操作前的值。updateAndGet(int index, LongUnaryOperator updateFunction)
: 应用给定的一元操作函数(updateFunction)到指定索引位置的元素上,并返回操作后的结果。getAndSet(int index, long newValue)
: 获取指定索引位置的当前值,并将其设置为新值(newValue)。返回旧值。weakCompareAndSet(int index, long expect, long update)
: 如果指定索引位置的当前值等于预期值(expect),则尝试将其更新为新值(update)。返回一个布尔值表示是否成功更新。incrementAndGet(int index)
: 获取指定索引位置的当前值,将其自增1,然后将结果写回该位置,最后返回操作后的值。toString()
: 返回数组的字符串表示形式。byteOffset(int index)
: 返回指定索引位置的字节偏移量。decrementAndGet(int index)
: 获取指定索引位置的当前值,将其自减1,然后将结果写回该位置,最后返回操作后的值。get(int index)
: 获取指定索引位置的值。compareAndSetRaw(long offset, long expect, long update)
: 如果指定偏移量处的当前值等于预期值(expect),则将其更新为新值(update)。返回一个布尔值表示是否成功更新。