Java的Atomic原子類詳解
Java SDK 并發(fā)包里提供了豐富的原子類,我們可以將其分為五個類別,這五個類別提供的方法基本上是相似的,并且每個類別都有若干原子類。
- 對基本數(shù)據(jù)類型的變量值進(jìn)行原子更新;
- 對對象變量的指向進(jìn)行原子更新;
- 對數(shù)組里面的的元素進(jìn)行原子更新;
- 原子化的對象屬性更新器;
- 原子化的累加器。
基本數(shù)據(jù)類型
AtomicBoolean、AtomicLong、AtomicInteger 這三個類提供了一些對基本數(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) // 以下四個方法新值可以通過傳入函數(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 這三個類提供了一些對對象變量的指向進(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)一個線程在進(jìn)行 CAS 操作時(shí),另一個線程可能會在此期間修改了同一個共享變量的值,然后又將其改回原來的值。這種情況下,CAS 操作就無法檢測到共享變量值的變化,從而導(dǎo)致 ABA 問題。如果我們僅僅在寫回?cái)?shù)據(jù)前判斷數(shù)值是 A,可能導(dǎo)致不合理的寫回操作。AtomicStampedReference 和 AtomicMarkableReference 這兩個原子類可以解決 ABA 問題。
- AtomicStampedReference 通過為對象引用建立類似版本號(stamp)的方式,來解決 ABA 問題。
- AtomicStampedReference 實(shí)現(xiàn)的 CAS 方法增加了版本號參數(shù)AtomicMarkableReference 的實(shí)現(xiàn)機(jī)制則更簡單,將版本號簡化成了一個 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 這三個類提供了一些對數(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。
這三個類提供了一些對對象的屬性進(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。這四個類僅僅用來在多線程環(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)建了一個 LongAccumulator 對象,初始值為1,累加函數(shù)為 (x, y) -> x * y,即每次累加都將之前的結(jié)果與新的值相乘。然后,我們累加了三個數(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)文章
利用Java獲取文件名、類名、方法名和行號的方法小結(jié)
這篇文章運(yùn)用實(shí)例代碼給大家介紹了利用Java怎樣獲取文件名、類名、方法名和行號,有需要的可以參考借鑒,下面一起來看看吧。2016-08-08idea創(chuàng)建spring boot工程及配置文件(最新推薦)
本文給大家介紹idea創(chuàng)建spring boot工程及配置文件,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-11-11