Java高并发编程:原子类
1. 并發(fā)編程概念
原子性
一個(gè)操作不能被再拆分了;即一個(gè)操作或者多個(gè)操作 要么全部執(zhí)行并且執(zhí)行的過程不會(huì)被任何因素打斷,要么就都不執(zhí)行。一個(gè)很經(jīng)典的例子就是銀行賬戶轉(zhuǎn)賬問題。
增量操作符++,不是原子的操作,它是先讀取舊值,然后寫回新值,包含2個(gè)操作
可見性
可見性是指當(dāng)多個(gè)線程訪問同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。
有序性
即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。但是指令重排序會(huì)影響到線程并發(fā)執(zhí)行的正確性
總結(jié)
要想并發(fā)程序正確地執(zhí)行,必須要保證原子性、可見性以及有序性。只要有一個(gè)沒有被保證,就有可能會(huì)導(dǎo)致程序運(yùn)行不正確。
并發(fā),并行,串行
并行是邏輯上同時(shí)發(fā)生,指在某一個(gè)時(shí)間內(nèi)同時(shí)運(yùn)行多個(gè)程序。
并發(fā)是物理上同時(shí)發(fā)生,指在某一個(gè)時(shí)間點(diǎn)同時(shí)運(yùn)行多個(gè)程序。
2. 原子類
java.util.concurrent.atomic包:原子類的小工具包,支持在單個(gè)變量上解除鎖的線程安全編程
2.1 如果同一個(gè)變量要被多個(gè)線程訪問,則可以使用該包中的類
- AtomicBoolean
- AtomicInteger
- AtomicLong
- AtomicReference
2.2 AtomicIntegerArray:操作數(shù)組里面的某個(gè)整數(shù)
2.3 同樣該包中提供了可以用于反射操作的類
- AtomicReferenceFieldUpdater
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
它們可以提供對(duì)關(guān)聯(lián)字段類型的訪問。
3. 原子性布爾 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() 方法允許你對(duì) AtomicBoolean 的當(dāng)前值與一個(gè)期望值進(jìn)行比較,如果當(dāng)前值等于期望值的話,將會(huì)對(duì) AtomicBoolean 設(shè)定一個(gè)新值。compareAndSet() 方法是原子性的,因此在同一時(shí)間之內(nèi)有單個(gè)線程執(zhí)行它。因此 compareAndSet() 方法可被用于一些類似于鎖的同步的簡(jiǎn)單實(shí)現(xiàn)。
以下是一個(gè) compareAndSet() 示例:
AtomicBoolean atomicBoolean = new AtomicBoolean(true); boolean expectedValue = true; boolean newValue = false; boolean wasNewValueSet = atomicBoolean.compareAndSet(expectedValue, newValue);本示例對(duì) AtomicBoolean 的當(dāng)前值與 true 值進(jìn)行比較,如果相等,將 AtomicBoolean 的值更新為 false。
4. 原子性整型 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,你可以這樣:
本示例將 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() 方法對(duì) 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)用場(chǎ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 的值減一,但它返回的是減一之前的值。
5. 原子性長(zhǎng)整型 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 如下:
將創(chuàng)建一個(gè)初始值為 0 的 AtomicLong。
如果你想創(chuàng)建一個(gè)指定初始值的 AtomicLong,可以:
本示例將 123 作為參數(shù)傳遞給 AtomicLong 的構(gòu)造子,后者將 AtomicLong 實(shí)例的初始值設(shè)置為 123。
獲取 AtomicLong 的值
你可以通過 get() 方法獲取 AtomicLong 的值。AtomicLong.get() 示例:
設(shè)置 AtomicLong 的值
你可以通過 set() 方法設(shè)置 AtomicLong 實(shí)例的值。一個(gè) AtomicLong.set() 的示例:
本示例新建了一個(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è)取決于你自己的場(chǎng)景。示例如下:
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 的值減一,但它返回的是減一之前的值。
6. 原子性引用型 AtomicReference
AtomicReference 提供了一個(gè)可以被原子性讀和寫的對(duì)象引用變量。原子性的意思是多個(gè)想要改變同一個(gè) AtomicReference 的線程不會(huì)導(dǎo)致 AtomicReference 處于不一致的狀態(tài)。AtomicReference 還有一個(gè) compareAndSet() 方法,通過它你可以將當(dāng)前引用于一個(gè)期望值(引用)進(jìn)行比較,如果相等,在該 AtomicReference 對(duì)象內(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();注意如何對(duì) get() 方法返回的引用強(qiáng)制轉(zhuǎn)換為 String。
泛型化的 AtomicReference 示例:
編譯器知道了引用的類型,所以我們無需再對(duì) get() 返回的引用進(jìn)行強(qiáng)制轉(zhuǎn)換了。
設(shè)置 AtomicReference 引用
你可以使用 get() 方法對(duì) 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ū)別在于編譯器將對(duì)你能夠設(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()來對(duì)存儲(chǔ)值和期望值進(jìn)行對(duì)比,如果二者一致,為 AtomicReference 設(shè)置一個(gè)新的引用。第一次比較,存儲(chǔ)的引用(initialReference)和期望的引用(initialReference)一致,所以一個(gè)新的引用(newReference)被設(shè)置給 AtomicReference,compareAndSet() 方法返回 true。第二次比較時(shí),存儲(chǔ)的引用(newReference)和期望的引用(initialReference)不一致,因此新的引用沒有被設(shè)置給 AtomicReference,compareAndSet() 方法返回 false。
總結(jié)
以上是生活随笔為你收集整理的Java高并发编程:原子类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java高并发编程:Callable、F
- 下一篇: Android 屏幕适配