摘要:顧名思義,是類型的線程安全原子類,可以在應用程序中以原子的方式更新值。創建對象先來看下對象的創建。也就是說當一個線程修改一個共享變量時,其它線程能立即讀到這個修改的值。
本文首發于一世流云的專欄:https://segmentfault.com/blog...一、AtomicInteger簡介
AtomicInteger,應該是atomic框架中用得最多的原子類了。顧名思義,AtomicInteger是Integer類型的線程安全原子類,可以在應用程序中以原子的方式更新int值。
1. 創建AtomicInteger對象先來看下AtomicInteger對象的創建。
AtomicInteger提供了兩個構造器,使用默認構造器時,內部int類型的value值為0:
AtomicInteger atomicInt = new AtomicInteger();
AtomicInteger類的內部并不復雜,所有的操作都針對內部的int值——value,并通過Unsafe類來實現線程安全的CAS操作:
來看下面這個示例程序:
public class Main { public static void main(String[] args) throws InterruptedException { AtomicInteger ai = new AtomicInteger(); Listlist = new ArrayList<>(); for (int i = 0; i < 10; i++) { Thread t = new Thread(new Accumlator(ai), "thread-" + i); list.add(t); t.start(); } for (Thread t : list) { t.join(); } System.out.println(ai.get()); } static class Accumlator implements Runnable { private AtomicInteger ai; Accumlator(AtomicInteger ai) { this.ai = ai; } @Override public void run() { for (int i = 0, len = 1000; i < len; i++) { ai.incrementAndGet(); } } } }
上述代碼使用了AtomicInteger的incrementAndGet方法,以原子的操作對int值進行自增,該段程序執行的最終結果為10000(10個線程,每個線程對AtomicInteger增加1000),如果不使用AtomicInteger,使用原始的int或Integer,最終結果值可能會小于10000(并發時讀到了過時的數據或存在值覆蓋的問題)。
我們來看下incrementAndGet內部:
內部調用了Unsafe類的getAndAddInt方法,以原子方式將value值增加1,然后返回增加前的原始值。
注意,上述是JDK1.8的實現,在JDK1.8之前,上述方法采用了自旋+CAS操作的方式:
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }3. AtomicInteger的特殊方法說明
AtomicInteger中有一個比較特殊的方法——lazySet:
lazySet方法是set方法的不可見版本。什么意思呢?
我們知道通過volatile修飾的變量,可以保證在多處理器環境下的“可見性”。也就是說當一個線程修改一個共享變量時,其它線程能立即讀到這個修改的值。volatile的實現最終是加了內存屏障:
保證寫volatile變量會強制把CPU寫緩存區的數據刷新到內存
讀volatile變量時,使緩存失效,強制從內存中讀取最新的值
由于內存屏障的存在,volatile變量還能阻止重排序
lazySet內部調用了Unsafe類的putOrderedInt方法,通過該方法對共享變量值的改變,不一定能被其他線程立即看到。也就是說以普通變量的操作方式來寫變量。
為什么會有這種奇怪方法?什么情況下需要使用lazySet呢?
考慮下面這樣一個場景:
private AtomicInteger ai = new AtomicInteger(); lock.lock(); try { // ai.set(1); } finally { lock.unlock(); }
由于鎖的存在:
lock()方法獲取鎖時,和volatile變量的讀操作一樣,會強制使CPU緩存失效,強制從內存讀取變量。
unlock()方法釋放鎖時,和volatile變量的寫操作一樣,會強制刷新CPU寫緩沖區,把緩存數據寫到主內存
所以,上述ai.set(1)可以用ai.lazySet(1)方法替換:
由鎖來保證共享變量的可見性,以設置普通變量的方式來修改共享變量,減少不必要的內存屏障,從而提高程序執行的效率。
二、類/接口說明 類聲明 構造器 接口聲明方法聲明 | 描述 |
---|---|
int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) | 使用IntBinaryOperator 對當前值和x進行計算,并更新當前值,返回計算后的新值 |
int addAndGet(int delta) | 以原子方式將給定值與當前值相加,返回相加后的新值 |
boolean compareAndSet(int expect, int update) | 如果當前值 == expect,則以原子方式將該值設置為給定的更新值(update) |
int decrementAndGet() | 以原子方式將當前值減 1,返回新值 |
int get() | 獲取當前值 |
int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) | 使用IntBinaryOperator 對當前值和x進行計算,并更新當前值,返回計算前的舊值 |
int getAndAdd(int delta) | 以原子方式將給定值與當前值相加,返回舊值 |
int getAndDecrement() | 以原子方式將當前值減 1,返回舊值 |
int getAndIncrement() | 以原子方式將當前值加 1,返回舊值 |
int getAndSet(int newValue) | 以原子方式設置為給定值,并返回舊值 |
int getAndUpdate(IntUnaryOperator updateFunction) | 使用IntBinaryOperator 對當前值進行計算,并更新當前值,返回計算前的舊值 |
int incrementAndGet() | 以原子方式將當前值加 1,返回新值 |
void lazySet(int newValue) | 設置為給定值,但不保證值的改變被其他線程立即看到 |
void set(int newValue) | 設置為給定值 |
int updateAndGet(IntUnaryOperator updateFunction) | 使用IntBinaryOperator 對當前值進行計算,并更新當前值,返回計算后的新值 |
boolean weakCompareAndSet(int expect, int update) | weakCompareAndSet無法保證除操作目標外的其他變量的執行順序( 編譯器和處理器為了優化程序性能而對指令序列進行重新排序 ),同時也無法保證這些變量的可見性。 |
與AtomicInteger類似的原子類還有AtomicBoolean和AtomicLong,底層都是通過Unsafe類做CAS操作,來原子的更新狀態值。可以參考Oracle官方文檔:https://docs.oracle.com/javas...,不再贅述。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76561.html
摘要:整個包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據一系列常見的多線程設計模式,設計了并發包,其中包下提供了一系列基礎的鎖工具,用以對等進行補充增強。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發于一世流云專欄:https...
摘要:注意原子數組并不是說可以讓線程以原子方式一次性地操作數組中所有元素的數組。類的方法返回指定類型數組的元素所占用的字節數。,是將轉換為進制,然后從左往右數連續的個數。 showImg(https://segmentfault.com/img/remote/1460000016012145); 本文首發于一世流云的專欄:https://segmentfault.com/blog... 一...
摘要:所謂,就是可以以一種線程安全的方式操作非線程安全對象的某些字段。我們來對上述代碼進行改造賬戶類改造引入通過操作字段調用方,并未做任何改變上述代碼,無論執行多少次,最終結果都是,因為這回是線程安全的。這也是整個包的設計理念之一。 showImg(https://segmentfault.com/img/remote/1460000016012109); 本文首發于一世流云的專欄:http...
摘要:本身不直接支持指針的操作,所以這也是該類命名為的原因之一。中的許多方法,內部其實都是類在操作。 showImg(https://segmentfault.com/img/remote/1460000016012251); 本文首發于一世流云的專欄:https://segmentfault.com/blog... 一、Unsafe簡介 在正式的開講 juc-atomic框架系列之前,有...
摘要:但是,有些操作會依賴于對象的變化過程,此時的解決思路一般就是使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號加一,那么就會變成。四的引入就是上面所說的加了版本號的。 showImg(https://segmentfault.com/img/remote/1460000016012188); 本文首發于一世流云的專欄:https://segmentfault.com/blo...
閱讀 2428·2021-11-25 09:43
閱讀 1203·2021-09-07 10:16
閱讀 2623·2021-08-20 09:38
閱讀 2947·2019-08-30 15:55
閱讀 1467·2019-08-30 13:21
閱讀 897·2019-08-29 15:37
閱讀 1450·2019-08-27 10:56
閱讀 2100·2019-08-26 13:45