Java并發(fā)編程-原子性變量

image.png

1. 原子性布爾 AtomicBoolean

AtomicBoolean 類為我們提供了一個(gè)可以用原子方式進(jìn)行讀和寫的布爾值抵乓,它還擁有一些先進(jìn)的原子性操作登澜,比如 compareAndSet()。AtomicBoolean 類位于 java.util.concurrent.atomic 包,完整類名是為 java.util.concurrent.atomic.AtomicBoolean纱新。本小節(jié)描述的 AtomicBoolean 是 Java 8 版本里的,而不是它第一次被引入的 Java 5 版本。

AtomicBoolean 背后的設(shè)計(jì)理念在我的《Java 并發(fā)指南》主題的《比較和交換》小節(jié)有解釋槐臀。

創(chuàng)建一個(gè) AtomicBoolean

你可以這樣創(chuàng)建一個(gè) AtomicBoolean:


AtomicBoolean atomicBoolean = new AtomicBoolean();  

以上示例新建了一個(gè)默認(rèn)值為 false 的 AtomicBoolean。如果你想要為 AtomicBoolean 實(shí)例設(shè)置一個(gè)顯式的初始值畴蹭,那么你可以將初始值傳給 AtomicBoolean 的構(gòu)造子:


AtomicBoolean atomicBoolean = new AtomicBoolean(true);  

獲取 AtomicBoolean 的值

你可以通過使用 get() 方法來獲取一個(gè) AtomicBoolean 的值坦仍。示例如下:


AtomicBoolean atomicBoolean = new AtomicBoolean(true);  
  
boolean value = atomicBoolean.get();  

以上代碼執(zhí)行后 value 變量的值將為 true。

設(shè)置 AtomicBoolean 的值

你可以通過使用 set() 方法來設(shè)置一個(gè) AtomicBoolean 的值叨襟。
示例如下:


AtomicBoolean atomicBoolean = new AtomicBoolean(true);  
  
atomicBoolean.set(false);  

以上代碼執(zhí)行后 AtomicBoolean 的值為 false繁扎。

交換 AtomicBoolean 的值

你可以通過 getAndSet() 方法來交換一個(gè) AtomicBoolean 實(shí)例的值。getAndSet() 方法將返回 AtomicBoolean 當(dāng)前的值糊闽,并將為 AtomicBoolean 設(shè)置一個(gè)新值梳玫。示例如下:


AtomicBoolean atomicBoolean = new AtomicBoolean(true);  
  
boolean oldValue = atomicBoolean.getAndSet(false);  

以上代碼執(zhí)行后 oldValue 變量的值為 true,atomicBoolean 實(shí)例將持有 false 值右犹。代碼成功將 AtomicBoolean 當(dāng)前值 ture 交換為 false提澎。

比較并設(shè)置 AtomicBoolean 的值

compareAndSet() 方法允許你對 AtomicBoolean 的當(dāng)前值與一個(gè)期望值進(jìn)行比較,如果當(dāng)前值等于期望值的話念链,將會(huì)對 AtomicBoolean 設(shè)定一個(gè)新值盼忌。compareAndSet() 方法是原子性的,因此在同一時(shí)間之內(nèi)有單個(gè)線程執(zhí)行它掂墓。因此 compareAndSet() 方法可被用于一些類似于鎖的同步的簡單實(shí)現(xiàn)谦纱。以下是一個(gè) compareAndSet() 示例:


AtomicBoolean atomicBoolean = new AtomicBoolean(true);  
  
boolean expectedValue = true;  
boolean newValue      = false;  
  
boolean wasNewValueSet = atomicBoolean.compareAndSet(  
    expectedValue, newValue);  

本示例對 AtomicBoolean 的當(dāng)前值與 true 值進(jìn)行比較,如果相等君编,將 AtomicBoolean 的值更新為 false跨嘉。

2. 原子性整型 AtomicInteger

AtomicInteger 類為我們提供了一個(gè)可以進(jìn)行原子性讀和寫操作的 int 變量,它還包含一系列先進(jìn)的原子性操作吃嘿,比如 compareAndSet()祠乃。AtomicInteger 類位于 java.util.concurrent.atomic 包梦重,因此其完整類名為 java.util.concurrent.atomic.AtomicInteger。本小節(jié)描述的 AtomicInteger 是 Java 8 版本里的跳纳,而不是它第一次被引入的 Java 5 版本忍饰。

AtomicInteger 背后的設(shè)計(jì)理念在我的《Java 并發(fā)指南》主題的《比較和交換》小節(jié)有解釋。

創(chuàng)建一個(gè) AtomicInteger

創(chuàng)建一個(gè) AtomicInteger 示例如下:


AtomicInteger atomicInteger = new AtomicInteger();  

本示例將創(chuàng)建一個(gè)初始值為 0 的 AtomicInteger寺庄。如果你想要?jiǎng)?chuàng)建一個(gè)給定初始值的 AtomicInteger艾蓝,你可以這樣:


AtomicInteger atomicInteger = new AtomicInteger(123);  

本示例將 123 作為參數(shù)傳給 AtomicInteger 的構(gòu)造子,它將設(shè)置 AtomicInteger 實(shí)例的初始值為 123斗塘。

獲取 AtomicInteger 的值

你可以使用 get() 方法獲取 AtomicInteger 實(shí)例的值赢织。示例如下:

AtomicInteger atomicInteger = new AtomicInteger(123);  
  
int theValue = atomicInteger.get();  

設(shè)置 AtomicInteger 的值

你可以通過 set() 方法對 AtomicInteger 的值進(jìn)行重新設(shè)置。以下是 AtomicInteger.set() 示例:


AtomicInteger atomicInteger = new AtomicInteger(123);  
  
atomicInteger.set(234);  

以上示例創(chuàng)建了一個(gè)初始值為 123 的 AtomicInteger馍盟,而在第二行將其值更新為 234于置。

比較并設(shè)置 AtomicInteger 的值

AtomicInteger 類也通過了一個(gè)原子性的 compareAndSet() 方法。這一方法將 AtomicInteger 實(shí)例的當(dāng)前值與期望值進(jìn)行比較贞岭,如果二者相等八毯,為 AtomicInteger 實(shí)例設(shè)置一個(gè)新值。AtomicInteger.compareAndSet() 代碼示例:

AtomicInteger atomicInteger = new AtomicInteger(123);  
  
int expectedValue = 123;  
int newValue      = 234;  
atomicInteger.compareAndSet(expectedValue, newValue);  

本示例首先新建一個(gè)初始值為 123 的 AtomicInteger 實(shí)例瞄桨。然后將 AtomicInteger 與期望值 123 進(jìn)行比較话速,如果相等,將 AtomicInteger 的值更新為 234芯侥。

增加 AtomicInteger 值

AtomicInteger 類包含有一些方法泊交,通過它們你可以增加 AtomicInteger 的值,并獲取其值柱查。這些方法如下:

  • addAndGet()
  • getAndAdd()
  • getAndIncrement()
  • incrementAndGet()

第一個(gè) addAndGet() 方法給 AtomicInteger 增加了一個(gè)值廓俭,然后返回增加后的值。getAndAdd() 方法為 AtomicInteger 增加了一個(gè)值唉工,但返回的是增加以前的 AtomicInteger 的值研乒。具體使用哪一個(gè)取決于你的應(yīng)用場景。以下是這兩種方法的示例:


AtomicInteger atomicInteger = new AtomicInteger();  
  
  
System.out.println(atomicInteger.getAndAdd(10));  
System.out.println(atomicInteger.addAndGet(10));  

本示例將打印出 0 和 20酵紫。例子中告嘲,第二行拿到的是加 10 之前的 AtomicInteger 的值。加 10 之前的值是 0奖地。第三行將 AtomicInteger 的值再加 10橄唬,并返回加操作之后的值。該值現(xiàn)在是為 20参歹。你當(dāng)然也可以使用這倆方法為 AtomicInteger 添加負(fù)值仰楚。結(jié)果實(shí)際是一個(gè)減法操作。getAndIncrement() 和 incrementAndGet() 方法類似于 getAndAdd() 和 addAndGet(),但每次只將 AtomicInteger 的值加 1僧界。

減小 AtomicInteger 的值

AtomicInteger 類還提供了一些減小 AtomicInteger 的值的原子性方法侨嘀。這些方法是:

  • decrementAndGet()
  • getAndDecrement()

decrementAndGet() 將 AtomicInteger 的值減一,并返回減一后的值捂襟。getAndDecrement() 也將 AtomicInteger 的值減一咬腕,但它返回的是減一之前的值。

3. 原子性長整型 AtomicLong

AtomicLong 類為我們提供了一個(gè)可以進(jìn)行原子性讀和寫操作的 long 變量葬荷,它還包含一系列先進(jìn)的原子性操作涨共,比如 compareAndSet()AtomicLong 類位于 java.util.concurrent.atomic 包,因此其完整類名為 java.util.concurrent.atomic.AtomicLong宠漩。本小節(jié)描述的 AtomicLong 是 Java 8 版本里的举反,而不是它第一次被引入的 Java 5 版本。

AtomicLong 背后的設(shè)計(jì)理念在我的《Java 并發(fā)指南》主題的《比較和交換》小節(jié)有解釋扒吁。

創(chuàng)建一個(gè) AtomicLong

創(chuàng)建一個(gè) AtomicLong 如下:


AtomicLong atomicLong = new AtomicLong();  

將創(chuàng)建一個(gè)初始值為 0 的 AtomicLong火鼻。如果你想創(chuàng)建一個(gè)指定初始值的 AtomicLong,可以:


AtomicLong atomicLong = new AtomicLong(123);  

本示例將 123 作為參數(shù)傳遞給 AtomicLong 的構(gòu)造子雕崩,后者將 AtomicLong 實(shí)例的初始值設(shè)置為 123魁索。

獲取 AtomicLong 的值

你可以通過 get() 方法獲取 AtomicLong 的值。AtomicLong.get() 示例:


AtomicLong atomicLong = new AtomicLong(123);  
  
long theValue = atomicLong.get();  

設(shè)置 AtomicLong 的值

你可以通過 set() 方法設(shè)置 AtomicLong 實(shí)例的值盼铁。一個(gè) AtomicLong.set() 的示例:


AtomicLong atomicLong = new AtomicLong(123);  
  
atomicLong.set(234);  

本示例新建了一個(gè)初始值為 123 的 AtomicLong蛾默,第二行將其值設(shè)置為 234。

比較并設(shè)置 AtomicLong 的值

AtomicLong 類也有一個(gè)原子性的 compareAndSet() 方法捉貌。這一方法將 AtomicLong 實(shí)例的當(dāng)前值與一個(gè)期望值進(jìn)行比較,如果兩種相等冬念,為 AtomicLong 實(shí)例設(shè)置一個(gè)新值趁窃。AtomicLong.compareAndSet() 使用示例:


AtomicLong atomicLong = new AtomicLong(123);  
  
long expectedValue = 123;  
long newValue      = 234;  
atomicLong.compareAndSet(expectedValue, newValue);  

本示例新建了一個(gè)初始值為 123 的 AtomicLong。然后將 AtomicLong 的當(dāng)前值與期望值 123 進(jìn)行比較急前,如果相等的話醒陆,AtomicLong 的新值將變?yōu)?234。

增加 AtomicLong 值

AtomicLong 具備一些能夠增加 AtomicLong 的值并返回自身值的方法裆针。這些方法如下:

  • addAndGet()
  • getAndAdd()
  • getAndIncrement()
  • incrementAndGet()

第一個(gè)方法 addAndGet() 將 AtomicLong 的值加一個(gè)數(shù)字刨摩,并返回增加后的值。第二個(gè)方法 getAndAdd() 也將 AtomicLong 的值加一個(gè)數(shù)字世吨,但返回的是增加前的 AtomicLong 的值澡刹。具體使用哪一個(gè)取決于你自己的場景。示例如下:


AtomicLong atomicLong = new AtomicLong();  
  
  
System.out.println(atomicLong.getAndAdd(10));  
System.out.println(atomicLong.addAndGet(10));  

本示例將打印出 0 和 20耘婚。例子中罢浇,第二行拿到的是加 10 之前的 AtomicLong 的值。加 10 之前的值是 0。第三行將 AtomicLong 的值再加 10嚷闭,并返回加操作之后的值攒岛。該值現(xiàn)在是為 20。你當(dāng)然也可以使用這倆方法為 AtomicLong 添加負(fù)值胞锰。結(jié)果實(shí)際是一個(gè)減法操作灾锯。getAndIncrement() 和 incrementAndGet() 方法類似于 getAndAdd() 和 addAndGet(),但每次只將 AtomicLong 的值加 1嗅榕。

減小 AtomicLong 的值

AtomicLong 類還提供了一些減小 AtomicLong 的值的原子性方法顺饮。這些方法是:

  • decrementAndGet()
  • getAndDecrement()

decrementAndGet() 將 AtomicLong 的值減一,并返回減一后的值誊册。getAndDecrement() 也將 AtomicLong 的值減一领突,但它返回的是減一之前的值。

4. 原子性引用型 AtomicReference

AtomicReference 提供了一個(gè)可以被原子性讀和寫的對象引用變量案怯。原子性的意思是多個(gè)想要改變同一個(gè) AtomicReference 的線程不會(huì)導(dǎo)致 AtomicReference 處于不一致的狀態(tài)君旦。AtomicReference 還有一個(gè) compareAndSet() 方法,通過它你可以將當(dāng)前引用于一個(gè)期望值(引用)進(jìn)行比較嘲碱,如果相等金砍,在該 AtomicReference 對象內(nèi)部設(shè)置一個(gè)新的引用。

創(chuàng)建一個(gè) AtomicReference

創(chuàng)建 AtomicReference 如下:


AtomicReference atomicReference = new AtomicReference();  

如果你需要使用一個(gè)指定引用創(chuàng)建 AtomicReference麦锯,可以:


String initialReference = "the initially referenced string";  
AtomicReference atomicReference = new AtomicReference(initialReference);  

創(chuàng)建泛型 AtomicReference

你可以使用 Java 泛型來創(chuàng)建一個(gè)泛型 AtomicReference恕稠。示例:


AtomicReference<String> atomicStringReference =  
    new AtomicReference<String>();  

你也可以為泛型 AtomicReference 設(shè)置一個(gè)初始值。示例:


String initialReference = "the initially referenced string";  
AtomicReference<String> atomicStringReference =  
    new AtomicReference<String>(initialReference);  

獲取 AtomicReference 引用

你可以通過 AtomicReference 的 get() 方法來獲取保存在 AtomicReference 里的引用扶欣。如果你的 AtomicReference 是非泛型的鹅巍,get() 方法將返回一個(gè) Object 類型的引用。如果是泛型化的料祠,get() 將返回你創(chuàng)建 AtomicReference 時(shí)聲明的那個(gè)類型骆捧。先來看一個(gè)非泛型的 AtomicReference get() 示例:


AtomicReference atomicReference = new AtomicReference("first value referenced");  
  
String reference = (String) atomicReference.get();  

注意如何對 get() 方法返回的引用強(qiáng)制轉(zhuǎn)換為 String。泛型化的 AtomicReference 示例:

AtomicReference<String> atomicReference =   
     new AtomicReference<String>("first value referenced");  
  
String reference = atomicReference.get();  

編譯器知道了引用的類型髓绽,所以我們無需再對 get() 返回的引用進(jìn)行強(qiáng)制轉(zhuǎn)換了敛苇。

設(shè)置 AtomicReference 引用

你可以使用 get() 方法對 AtomicReference 里邊保存的引用進(jìn)行設(shè)置。如果你定義的是一個(gè)非泛型 AtomicReference顺呕,set() 將會(huì)以一個(gè) Object 引用作為參數(shù)枫攀。如果是泛型化的 AtomicReference,set() 方法將只接受你定義給的類型株茶。AtomicReference set() 示例:


AtomicReference atomicReference =   
     new AtomicReference();  
      
atomicReference.set("New object referenced");  

這個(gè)看起來非泛型和泛型化的沒啥區(qū)別来涨。真正的區(qū)別在于編譯器將對你能夠設(shè)置給一個(gè)泛型化的 AtomicReference 參數(shù)類型進(jìn)行限制。

比較并設(shè)置 AtomicReference 引用

AtomicReference 類具備了一個(gè)很有用的方法:compareAndSet()启盛。compareAndSet() 可以將保存在 AtomicReference 里的引用于一個(gè)期望引用進(jìn)行比較扫夜,如果兩個(gè)引用是一樣的(并非 equals() 的相等,而是 == 的一樣),將會(huì)給 AtomicReference 實(shí)例設(shè)置一個(gè)新的引用笤闯。

如果 compareAndSet() 為 AtomicReference 設(shè)置了一個(gè)新的引用堕阔,compareAndSet() 將返回 true。否則 compareAndSet() 返回 false颗味。AtomicReference compareAndSet() 示例:


String initialReference = "initial value referenced";  
  
AtomicReference<String> atomicStringReference =  
    new AtomicReference<String>(initialReference);  
  
String newReference = "new value referenced";  
boolean exchanged = atomicStringReference.compareAndSet(initialReference, newReference);  
System.out.println("exchanged: " + exchanged);  
  
exchanged = atomicStringReference.compareAndSet(initialReference, newReference);  
System.out.println("exchanged: " + exchanged);  

本示例創(chuàng)建了一個(gè)帶有一個(gè)初始引用的泛型化的 AtomicReference超陆。之后兩次調(diào)用 comparesAndSet()來對存儲(chǔ)值和期望值進(jìn)行對比,如果二者一致浦马,為 AtomicReference 設(shè)置一個(gè)新的引用时呀。第一次比較,存儲(chǔ)的引用(initialReference)和期望的引用(initialReference)一致晶默,所以一個(gè)新的引用(newReference)被設(shè)置給 AtomicReference谨娜,compareAndSet() 方法返回 true。第二次比較時(shí)磺陡,存儲(chǔ)的引用(newReference)和期望的引用(initialReference)不一致趴梢,因此新的引用沒有被設(shè)置給 AtomicReference,compareAndSet() 方法返回 false币他。

原文鏈接:http://tutorials.jenkov.com/java-util-concurrent/index.html坞靶。


個(gè)人介紹:

** 高廣超** :多年一線互聯(lián)網(wǎng)研發(fā)與架構(gòu)設(shè)計(jì)經(jīng)驗(yàn),擅長設(shè)計(jì)與落地高可用蝴悉、高性能互聯(lián)網(wǎng)架構(gòu)彰阴。目前就職于美團(tuán)網(wǎng),負(fù)責(zé)核心業(yè)務(wù)研發(fā)工作拍冠。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尿这,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子庆杜,更是在濱河造成了極大的恐慌妻味,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欣福,死亡現(xiàn)場離奇詭異,居然都是意外死亡焦履,警方通過查閱死者的電腦和手機(jī)拓劝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘉裤,“玉大人郑临,你說我怎么就攤上這事⌒汲瑁” “怎么了厢洞?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我躺翻,道長丧叽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任公你,我火速辦了婚禮踊淳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陕靠。我一直安慰自己迂尝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布剪芥。 她就那樣靜靜地躺著垄开,像睡著了一般。 火紅的嫁衣襯著肌膚如雪税肪。 梳的紋絲不亂的頭發(fā)上溉躲,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機(jī)與錄音寸认,去河邊找鬼签财。 笑死,一個(gè)胖子當(dāng)著我的面吹牛偏塞,可吹牛的內(nèi)容都是我干的唱蒸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼灸叼,長吁一口氣:“原來是場噩夢啊……” “哼神汹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起古今,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤屁魏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后捉腥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氓拼,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年抵碟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桃漾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拟逮,死狀恐怖撬统,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情敦迄,我是刑警寧澤恋追,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布凭迹,位于F島的核電站,受9級(jí)特大地震影響苦囱,放射性物質(zhì)發(fā)生泄漏嗅绸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一沿彭、第九天 我趴在偏房一處隱蔽的房頂上張望朽砰。 院中可真熱鬧,春花似錦喉刘、人聲如沸瞧柔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽造锅。三九已至,卻和暖如春廉邑,著一層夾襖步出監(jiān)牢的瞬間哥蔚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工蛛蒙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糙箍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓牵祟,卻偏偏與公主長得像深夯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子诺苹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容