Java的Atomic原子類詳解
Java SDK 并發(fā)包里提供了豐富的原子類,我們可以將其分為五個(gè)類別,這五個(gè)類別提供的方法基本上是相似的,并且每個(gè)類別都有若干原子類。
- 對基本數(shù)據(jù)類型的變量值進(jìn)行原子更新;
- 對對象變量的指向進(jìn)行原子更新;
- 對數(shù)組里面的的元素進(jìn)行原子更新;
- 原子化的對象屬性更新器;
- 原子化的累加器。

基本數(shù)據(jù)類型
AtomicBoolean、AtomicLong、AtomicInteger 這三個(gè)類提供了一些對基本數(shù)據(jù)類型的變量值進(jìn)行原子更新的方法。
這些類提供的方法是相似的,主要有(以 AtomicLong 為例):
// 原子化的 i++ long getAndIncrement() // 原子化的 i-- long getAndDecrement() // 原子化的 ++i long incrementAndGet() // 原子化的 --i long decrementAndGet() // 原子化的 i+=delta,返回值為+=前的i值 long getAndAdd(long delta) // 原子化的 i+=delta,返回值為+=后的i值 long addAndGet(delta) // CAS操作。如果寫回成功返回true,否則返回false boolean compareAndSet(long expect, long update) // 以下四個(gè)方法新值可以通過傳入函數(shù)式接口(func函數(shù))來計(jì)算 long getAndUpdate(LongUnaryOperator updateFunction) long updateAndGet(LongUnaryOperator updateFunction) long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction)
// 演示 getAndUpdate() 方法的使用
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(0);
long result = atomicLong.getAndUpdate(new LongUnaryOperator() {
@Override
public long applyAsLong(long operand) {
return operand + 1;
}
});
}對象引用類型
AtomicReference、AtomicStampedReference、AtomicMarkableReference 這三個(gè)類提供了一些對對象變量的指向進(jìn)行原子更新的方法。如果需要對對象的屬性進(jìn)行原子更像,那么可以使用原子化的對象屬性更新器。
public class ClassName {
AtomicReference<Employee> employeeAR = new AtomicReference<>(new Employee("小明"));
public void methodName() {
Employee oldVal = employeeAR.get();
Employee newVal = new Employee(oldVal.getName());
employeeAR.compareAndSet(oldVal, newVal);
}
}對象引用的原子化更新需要重點(diǎn)關(guān)注 ABA 問題。當(dāng)一個(gè)線程在進(jìn)行 CAS 操作時(shí),另一個(gè)線程可能會在此期間修改了同一個(gè)共享變量的值,然后又將其改回原來的值。這種情況下,CAS 操作就無法檢測到共享變量值的變化,從而導(dǎo)致 ABA 問題。如果我們僅僅在寫回?cái)?shù)據(jù)前判斷數(shù)值是 A,可能導(dǎo)致不合理的寫回操作。AtomicStampedReference 和 AtomicMarkableReference 這兩個(gè)原子類可以解決 ABA 問題。
- AtomicStampedReference 通過為對象引用建立類似版本號(stamp)的方式,來解決 ABA 問題。
- AtomicStampedReference 實(shí)現(xiàn)的 CAS 方法增加了版本號參數(shù)AtomicMarkableReference 的實(shí)現(xiàn)機(jī)制則更簡單,將版本號簡化成了一個(gè) Boolean 值
boolean compareAndSet(V expectedReference, V newReference,
int expectedStamp, int newStamp)
boolean compareAndSet(V expectedReference, V newReference,
boolean expectedMark, boolean newMark)數(shù)組
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 這三個(gè)類提供了一些對數(shù)組里面的的元素進(jìn)行原子更新的方法。
public class ClassName {
AtomicLongArray atomicLongArray = new AtomicLongArray(new long[]{0, 1});
public void methodName() {
int index = 0;
long oldVal = atomicLongArray.get(index);
long newVal = oldVal + 1;
atomicLongArray.compareAndSet(index, oldVal, newVal);
}
}原子化的對象屬性更新器
原子化的對象屬性更新器有:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。
這三個(gè)類提供了一些對對象的屬性進(jìn)行原子更新的方法。這些方法是利用反射機(jī)制實(shí)現(xiàn)的。
public class ClassName {
AtomicIntegerFieldUpdater<Employee> fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(Employee.class, "salary");
Employee employee = new Employee("小明", 1000);
public void methodName() {
int oldVal = employee.getSalary();
int newVal = oldVal + 1000;
fieldUpdater.compareAndSet(employee, oldVal, newVal);
}
}需要注意的是:
- 對象屬性的類型必須是基本數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型對應(yīng)的包裝類。如果對象屬性的類型不是基本數(shù)據(jù)類型,newUpdater() 方法會拋出 IllegalArgumentException 運(yùn)行時(shí)異常。
- 對象的屬性必須是 volatile 類型的,只有這樣才能保證可見性。如果對象的屬性不是 volatile 類型的,newUpdater() 方法會拋出 IllegalArgumentException 運(yùn)行時(shí)異常。
// AtomicIntegerFieldUpdater 類中的代碼
if (field.getType() != int.class) {
throw new IllegalArgumentException("Must be integer type");
}
if (!Modifier.isVolatile(modifiers)) {
throw new IllegalArgumentException("Must be volatile type");
}原子化的累加器
原子化的累加器有:LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator。這四個(gè)類僅僅用來在多線程環(huán)境下,執(zhí)行累加操作。
相比原子化的基本數(shù)據(jù)類型,原子化的累加器的速度更快,但是它(原子化的累加器)不支持 compareAndSet() 方法。如果僅僅需要累加操作,使用原子化的累加器性能會更好。
原子化的累加器的本質(zhì)是空間換時(shí)間。
LongAdder 的使用示例如下所示:
public static void main(String[] args) {
LongAdder adder = new LongAdder();
// 初始化
adder.add(1);
// 累加
for (int i = 0; i < 100; i++) {
adder.increment();
}
long sum = adder.sum();
}LongAccumulator 與 LongAdder 類似,但 LongAccumulator 提供了更加靈活的累加操作,可以自定義累加函數(shù)。
使用示例如下所示。在使用示例中,我們創(chuàng)建了一個(gè) LongAccumulator 對象,初始值為1,累加函數(shù)為 (x, y) -> x * y,即每次累加都將之前的結(jié)果與新的值相乘。然后,我們累加了三個(gè)數(shù)值,最后輸出累加結(jié)果。由于累加函數(shù)是(x, y) -> x * y,所以最終的累加結(jié)果為1 * 5 * 10 * 20 = 1000。
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left * right;
}
}, 1);
// 初始值為1,累加函數(shù)為(x, y) -> x * y
accumulator.accumulate(5);
accumulator.accumulate(10);
accumulator.accumulate(20);
// 累加結(jié)果為 1 * 5 * 10 * 20 = 1000
long result = accumulator.get();
}到此這篇關(guān)于Java的Atomic原子類的文章就介紹到這了,更多相關(guān)Java Atomic原子類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 深入了解Java atomic原子類的使用方法和原理
- Java多線程Atomic包操作原子變量與原子類詳解
- Java concurrency之AtomicReference原子類_動力節(jié)點(diǎn)Java學(xué)院整理
- Java concurrency之AtomicLong原子類_動力節(jié)點(diǎn)Java學(xué)院整理
- Java concurrency之AtomicLongFieldUpdater原子類_動力節(jié)點(diǎn)Java學(xué)院整理
- Java concurrency之AtomicLongArray原子類_動力節(jié)點(diǎn)Java學(xué)院整理
相關(guān)文章
JDK1.6“新“特性Instrumentation之JavaAgent(推薦)
這篇文章主要介紹了JDK1.6“新“特性Instrumentation之JavaAgent,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
如何解決java:錯(cuò)誤:無效的源發(fā)行版:17問題
這篇文章主要介紹了如何解決java:錯(cuò)誤:無效的源發(fā)行版:17問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Java實(shí)現(xiàn)HTML轉(zhuǎn)PDF及合并PDF文件詳細(xì)教程
之前在工作中遇到過一個(gè)需求,是將用戶填寫的申報(bào),由固定模板的來生成pdf,后來想到先把內(nèi)容生成html,然后再把html格式的內(nèi)容轉(zhuǎn)換成pdf,這篇文章主要介紹了Java實(shí)現(xiàn)HTML轉(zhuǎn)PDF及合并PDF文件的相關(guān)資料,需要的朋友可以參考下2025-07-07
Java基于UDP協(xié)議實(shí)現(xiàn)簡單的聊天室程序
這篇文章主要為大家詳細(xì)介紹了Java基于UDP協(xié)議實(shí)現(xiàn)簡單的聊天室程序的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07
CountDownLatch和Atomic原子操作類源碼解析
這篇文章主要為大家介紹了CountDownLatch和Atomic原子操作類的源碼解析以及理解應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
springboot?靜態(tài)方法中使用@Autowired注入方式
這篇文章主要介紹了springboot?靜態(tài)方法中使用@Autowired注入方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
java兩個(gè)數(shù)組合并為一個(gè)數(shù)組的幾種方法
這篇文章主要給大家介紹了關(guān)于java兩個(gè)數(shù)組合并為一個(gè)數(shù)組的幾種方法,最近在寫代碼時(shí)遇到了需要合并兩個(gè)數(shù)組的需求,文中將每種方法都介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07

