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