JDK中的Atomic包中的类及使用
引言
Java從JDK1.5開始提供了java.util.concurrent.atomic包,方便程序員在多線程環(huán)境下,無鎖的進行原子操作。原子變量的底層使用了處理器提供的原子指令,但是不同的CPU架構(gòu)可能提供的原子指令不一樣,也有可能需要某種形式的內(nèi)部鎖,所以該方法不能絕對保證線程不被阻塞。
Atomic包介紹
在JDK1.8 Atomic包里一共有17個類,四種原子更新方式,分別是原子更新基本類型,原子更新數(shù)組,原子更新引用和原子更新字段。Atomic包里的類基本都是使用Unsafe實現(xiàn)的包裝類。
1. 原子更新基本類型類
用于通過原子的方式更新基本類型,Atomic包提供了以下三個類:
- AtomicBoolean:原子更新布爾類型。
- AtomicInteger:原子更新整型。
- AtomicLong:原子更新長整型。
AtomicInteger的常用方法如下:
- int addAndGet(int delta) :以原子方式將輸入的數(shù)值與實例中的值(AtomicInteger里的value)相加,并返回結(jié)果
- boolean compareAndSet(int expect, int update) :如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入的值。
- int getAndIncrement():以原子方式將當前值加1,注意:這里返回的是自增前的值。
- void lazySet(int newValue):最終會設(shè)置成newValue,使用lazySet設(shè)置值后,可能導(dǎo)致其他線程在之后的一小段時間內(nèi)還是可以讀到舊的值。關(guān)于該方法的更多信息可以參考并發(fā)網(wǎng)翻譯的一篇文章《AtomicLong.lazySet是如何工作的?》
- int getAndSet(int newValue):以原子方式設(shè)置為newValue的值,并返回舊值。
AtomicInteger例子代碼如下:
| 1 2 3 4 5 6 7 8 | import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest { ????static AtomicInteger ai = new AtomicInteger(1); ????public static void main(String[] args) { ????????System.out.println(ai.getAndIncrement()); ????????System.out.println(ai.get()); ????} } |
輸出
| 1 2 | 1 2 |
餐后甜點
Atomic包提供了三種基本類型的原子更新,但是Java的基本類型里還有char,float和double等。那么問題來了,如何原子的更新其他的基本類型呢?Atomic包里的類基本都是使用Unsafe實現(xiàn)的,讓我們一起看下Unsafe的源碼,發(fā)現(xiàn)Unsafe只提供了三種CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源碼,發(fā)現(xiàn)其是先把Boolean轉(zhuǎn)換成整型,再使用compareAndSwapInt進行CAS,所以原子更新double也可以用類似的思路來實現(xiàn)。
2. 原子更新數(shù)組類
通過原子的方式更新數(shù)組里的某個元素,Atomic包提供了以下三個類:
- AtomicIntegerArray:原子更新整型數(shù)組里的元素。
- AtomicLongArray:原子更新長整型數(shù)組里的元素。
- AtomicReferenceArray:原子更新引用類型數(shù)組里的元素。
AtomicIntegerArray類主要是提供原子的方式更新數(shù)組里的整型,其常用方法如下
- int addAndGet(int i, int delta):以原子方式將輸入值與數(shù)組中索引i的元素相加。
- boolean compareAndSet(int i, int expect, int update):如果當前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值。
實例代碼如下:
| 1 2 3 4 5 6 7 8 9 10 | public class AtomicIntegerArrayTest {? ????static int[] value = new int[] { 1, 2 };? ????static AtomicIntegerArray ai = new AtomicIntegerArray(value);? ????public static void main(String[] args) { ????????ai.getAndSet(0, 3); ????????System.out.println(ai.get(0)); ????????????????System.out.println(value[0]); ????}? } |
輸出
| 1 2 | 3 1 |
AtomicIntegerArray類需要注意的是,數(shù)組value通過構(gòu)造方法傳遞進去,然后AtomicIntegerArray會將當前數(shù)組復(fù)制一份,所以當AtomicIntegerArray對內(nèi)部的數(shù)組元素進行修改時,不會影響到傳入的數(shù)組。
3. 原子更新引用類型
原子更新基本類型的AtomicInteger,只能更新一個變量,如果要原子的更新多個變量,就需要使用這個原子更新引用類型提供的類。Atomic包提供了以下三個類:
- AtomicReference:原子更新引用類型。
- AtomicReferenceFieldUpdater:原子更新引用類型里的字段。
- AtomicMarkableReference:原子更新帶有標記位的引用類型。可以原子的更新一個布爾類型的標記位和引用類型。構(gòu)造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
AtomicReference的使用例子代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class AtomicReferenceTest {? ????public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();? ????public static void main(String[] args) { ????????User user = new User("conan", 15); ????????atomicUserRef.set(user); ????????User updateUser = new User("Shinichi", 17); ????????atomicUserRef.compareAndSet(user, updateUser); ????????System.out.println(atomicUserRef.get().getName()); ????????System.out.println(atomicUserRef.get().getOld()); ????}? ????static class User { ????????private String name; ????????private int old;? ????????public User(String name, int old) { ????????????this.name = name; ????????????this.old = old; ????????}? ????????public String getName() { ????????????return name; ????????}? ????????public int getOld() { ????????????return old; ????????} ????} } |
輸出
| 1 2 | Shinichi 17 |
4. 原子更新字段類
如果我們只需要某個類里的某個字段,那么就需要使用原子更新字段類,Atomic包提供了以下三個類:
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFieldUpdater:原子更新長整型字段的更新器。
- AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來,可用于原子的更數(shù)據(jù)和數(shù)據(jù)的版本號,可以解決使用CAS進行原子更新時,可能出現(xiàn)的ABA問題。
原子更新字段類都是抽象類,每次使用都時候必須使用靜態(tài)方法newUpdater創(chuàng)建一個更新器。原子更新類的字段的必須使用public volatile修飾符。AtomicIntegerFieldUpdater的例子代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class AtomicIntegerFieldUpdaterTest {? ????private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater ????????????.newUpdater(User.class, "old");? ????public static void main(String[] args) { ????????User conan = new User("conan", 10); ????????System.out.println(a.getAndIncrement(conan)); ????????System.out.println(a.get(conan)); ????}? ????public static class User { ????????private String name; ????????public volatile int old;? ????????public User(String name, int old) { ????????????this.name = name; ????????????this.old = old; ????????}? ????????public String getName() { ????????????return name; ????????}? ????????public int getOld() { ????????????return old; ????????} ????} } |
輸出
| 1 2 | 10 11 |
總結(jié)
以上是生活随笔為你收集整理的JDK中的Atomic包中的类及使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 移动端适配--flexible.js
- 下一篇: Java爬虫之利用Jsoup自制简单的搜