Java中12个原子操作类
Java 從 JDK 1.5 開始提供了 java.util.concurrent.atomic 包(以下簡(jiǎn)稱Atomic包),這個(gè)包中的 原子操作類 提供了一種用法簡(jiǎn)單、性能高效、線程安全地更新一個(gè)變量的方式。
因?yàn)樽兞康念愋陀泻芏喾N,所以在 Atomic 包里一共提供了 12個(gè) 類,屬于以下 4 種類型的原子更新方式:
原子更新基本類型。
AtomicBoolean:原子更新布爾類型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新長(zhǎng)整型。
原子更新數(shù)組。
AtomicIntegerArray:原子更新整型數(shù)組里的元素。
AtomicLongArray:原子更新長(zhǎng)整型數(shù)組里的元素。
AtomicReferenceArray:原子更新引用類型數(shù)組里的元素。
原子更新引用。
AtomicReference:原子更新對(duì)象引用。
AtomicMarkableReference:原子更新帶有標(biāo)記位的對(duì)象引用。
AtomicStampedReference:原子更新帶有版本號(hào)的對(duì)象引用。
原子更新屬性(字段)。
AtomicIntegerFieldUpdater:原子更新volatile修飾的整型的字段的更新器。
AtomicLongFieldUpdater:原子更新volatile修飾的長(zhǎng)整型字段的更新器。
AtomicReferenceFieldUpdater:原子更新volatile修飾的引用類型里的字段的更新器。
Atomic 包里的類基本都是使用 Unsafe 實(shí)現(xiàn)的包裝類。
原子更新基本類型
AtomicBoolean:原子更新布爾類型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新長(zhǎng)整型。
以上3個(gè)類提供的方法幾乎一模一樣,所以本節(jié)僅以 AtomicInteger 為例進(jìn)行講解。
AtomicInteger 的常用方法如下:
int addAndGet(int delta):以原子方式將輸入的數(shù)值與實(shí)例中的值(AtomicInteger 里的 value)相加,并返回結(jié)果。
boolean compareAndSet(int expect,int update):如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入的值。
int getAndIncrement():以原子方式將當(dāng)前值加1,注意,這里返回的是自增前的值。
void lazySet(int newValue):最終會(huì)設(shè)置成 newValue,使用 lazySet 設(shè)置值后,可導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值。
int getAndSet(int newValue):以原子方式設(shè)置為 newValue 的值,并返回舊值。
示例
public static void main(String[] args) {AtomicInteger ai = new AtomicInteger(2);System.out.println("ai.get() = " + ai.get());System.out.println("ai.addAndGet(5) = " + ai.addAndGet(5));System.out.println("ai.get() = " + ai.get());System.out.println("ai.compareAndSet(ai.get(), 10) = " + ai.compareAndSet(ai.get(), 10));System.out.println("ai.get() = " + ai.get());System.out.println("ai.getAndIncrement() = " + ai.getAndIncrement());System.out.println("ai.get() = " + ai.get());ai.lazySet(8);System.out.println("ai.lazySet(8)");System.out.println("ai.get() = " + ai.get());System.out.println("ai.getAndSet(5) = " + ai.getAndSet(5));System.out.println("ai.get() = " + ai.get()); }輸出
ai.get() = 2 ai.addAndGet(5) = 7 ai.get() = 7 ai.compareAndSet(ai.get(), 10) = true ai.get() = 10 ai.getAndIncrement() = 10 ai.get() = 11 ai.lazySet(8) ai.get() = 8 ai.getAndSet(5) = 8 ai.get() = 5AtomicInteger 的 getAndIncrement()方法:
public final int getAndIncrement() {for (; ; ) {int current = get();int next = current + 1;if (compareAndSet(current, next)) {return current;}} } public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }for 循環(huán)體的先取得 AtomicInteger 里存儲(chǔ)的數(shù)值
對(duì) AtomicInteger 的當(dāng)前數(shù)值進(jìn)行 +1 操作,
關(guān)鍵是調(diào)用 compareAndSet 方法來(lái)進(jìn)行原子更新操作,該方法先檢查 當(dāng)前數(shù)值是否等于current ?
等于意味著 AtomicInteger 的值沒有被其他線程修改過(guò),則將 AtomicInteger 的當(dāng)前數(shù)值更新成 next的值。如果不等 compareAndSet 方法會(huì)返回 false,程序會(huì)進(jìn)入 for 循環(huán)重新進(jìn)行 compareAndSet 操作。Atomic 包提供了 3 種基本類型的原子更新,但是 Java 的基本類型里還有 char、float 和 double 等。
那么問(wèn)題來(lái)了,如何原子的更新其他的基本類型呢?
Atomic包里的類基本都是使用 Unsafe 實(shí)現(xiàn)的,讓我們一起看一下Unsafe的源碼:
/*** 如果當(dāng)前數(shù)值是expected,則原子的將Java變量更新成x** @return 如果更新成功則返回true*/ public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);public final native boolean compareAndSwapInt(Object o, long offset,int expected, int x);public final native boolean compareAndSwapLong(Object o, long offset,long expected, long x);綜合上述代碼,我們可以發(fā)現(xiàn) Unsafe 只提供了 3 種 CAS 方法:compareAndSwapObject、compareAndSwapInt 和 compareAndSwapLong,再看 AtomicBoolean 源碼,發(fā)現(xiàn)它是先把 Boolean 轉(zhuǎn)換成 整型,再使用 compareAndSwapInt 進(jìn)行 CAS,所以原子更新 char、float 和 double 變量也可以用類似的思路來(lái)實(shí)現(xiàn)。
原子更新數(shù)組
AtomicIntegerArray:原子更新整型數(shù)組里的元素。
AtomicLongArray:原子更新長(zhǎng)整型數(shù)組里的元素。
AtomicReferenceArray:原子更新引用類型數(shù)組里的元素。
以上幾個(gè)類提供的方法幾乎一樣,所以僅以 AtomicIntegerArray 為例進(jìn)行介紹:
AtomicIntegerArray 類主要是提供原子的方式更新數(shù)組里的整型。
常用方法如下:
int addAndGet(int i,int delta):以原子方式將輸入值與數(shù)組中索引i的元素相加。boolean compareAndSet(int i,int expect,int update):如果當(dāng)前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值。示例
public static void main(String[] args) {int[] value = new int[]{1, 2};AtomicIntegerArray ai = new AtomicIntegerArray(value);System.out.println("ai.getAndSet(0, 3)");ai.getAndSet(0, 3);System.out.println("ai.get(0) = " + ai.get(0));System.out.println("value[0] = " + value[0]);ai.compareAndSet(1, 2, 5);System.out.println("ai.compareAndSet(1, 2, 5)");System.out.println("ai.get(1) = " + ai.get(1)); }輸出
ai.getAndSet(0, 3) ai.get(0) = 3 value[0] = 1 ai.compareAndSet(1,2,5) ai.get(1) = 5注意:數(shù)組value通過(guò)構(gòu)造方法傳遞進(jìn)去,然后AtomicIntegerArray會(huì)將當(dāng)前數(shù)組復(fù)制一份,所以當(dāng)AtomicIntegerArray對(duì)內(nèi)部的數(shù)組元素進(jìn)行 修改 時(shí),不會(huì)影響傳入的數(shù)組。
原子更新引用
AtomicReference:原子更新對(duì)象引用。
AtomicMarkableReference:原子更新帶有標(biāo)記位的對(duì)象引用。
AtomicStampedReference:原子更新帶有版本號(hào)的對(duì)象引用。該類將整數(shù)值與引用關(guān)聯(lián)起來(lái),可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用 CAS 進(jìn)行原子更新時(shí)可能出現(xiàn)的 ABA問(wèn)題。
我們以AtomicReference為例進(jìn)行介紹。
示例
public class AtomicReferenceTest {public static AtomicReference<User> atomicUserRef = newAtomicReference<User>();public static void main(String[] args) {User user = new User("103style", 20);atomicUserRef.set(user);System.out.println("atomicUserRef.get() = " + atomicUserRef.get().toString());User updateUser = new User("xiaoke", 22);atomicUserRef.compareAndSet(user, updateUser);System.out.println("atomicUserRef.compareAndSet(user, updateUser);");System.out.println("atomicUserRef.get() = " + atomicUserRef.get().toString());}static class User {private String name;private int age;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "name='" + name + ", age=" + age;}} }輸出
atomicUserRef.get() = name='103style, age=20 atomicUserRef.compareAndSet(user, updateUser); atomicUserRef.get() = name='xiaoke, age=22原子更新屬性(字段)
AtomicIntegerFieldUpdater:原子更新volatile修飾的整型的字段的更新器。
AtomicLongFieldUpdater:原子更新volatile修飾的長(zhǎng)整型字段的更新器。
AtomicReferenceFieldUpdater:原子更新volatile修飾的引用類型里的字段的更新器。
要想原子地更新字段類需要兩步:
因?yàn)樵痈伦侄晤惗际浅橄箢?#xff0c;每次使用的時(shí)候必須使用靜態(tài)方法newUpdater()創(chuàng)建一個(gè)更新器,并且需要設(shè)置想要更新的類和屬性。更新類的字段(屬性)必須使用public volatile修飾符。我們以AstomicIntegerFieldUpdater 為例進(jìn)行講解。
示例
public class AtomicIntegerFieldUpdaterTest {public static void main(String[] args) {// 創(chuàng)建原子更新器,并設(shè)置需要更新的對(duì)象類和對(duì)象的屬性AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");// 設(shè)置柯南的年齡是10歲User conan = new User("conan", 10);// 柯南長(zhǎng)了一歲,但是仍然會(huì)輸出舊的年齡System.out.println(a.getAndIncrement(conan));// 輸出柯南現(xiàn)在的年齡System.out.println(a.get(conan));}public static class User {public volatile int age;private String name;public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}} }輸出
10 11了解更多歡迎點(diǎn)贊關(guān)注的喲!!!
總結(jié)
以上是生活随笔為你收集整理的Java中12个原子操作类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java 获取 URL 响应头信息
- 下一篇: 正则表达式的匹配规则