[TOC]
轉(zhuǎn)載自并發(fā)編程網(wǎng) – ifeve.com本文鏈接地址: Java中的Atomic包使用指南
本文首發(fā)于并發(fā)網(wǎng),作者:方騰飛
引言
Java從JDK1.5開(kāi)始提供了java.util.concurrent.atomic包必逆,方便程序員在多線程環(huán)境下样漆,無(wú)鎖的進(jìn)行原子操作人断。原子變量的底層使用了處理器提供的原子指令蚁署,但是不同的CPU架構(gòu)可能提供的原子指令不一樣绣版,也有可能需要某種形式的內(nèi)部鎖,所以該方法不能絕對(duì)保證線程不被阻塞鲤拿。
Atomic包介紹
在Atomic包里一共有12個(gè)類(lèi)假褪,四種原子更新方式,分別是原子更新基本類(lèi)型近顷,原子更新數(shù)組生音,原子更新引用和原子更新字段。Atomic包里的類(lèi)基本都是使用Unsafe實(shí)現(xiàn)的包裝類(lèi)窒升。
原子更新基本類(lèi)型類(lèi)
用于通過(guò)原子的方式更新基本類(lèi)型缀遍,Atomic包提供了以下三個(gè)類(lèi):
- AtomicBoolean:原子更新布爾類(lèi)型。
- AtomicInteger:原子更新整型饱须。
- AtomicLong:原子更新長(zhǎng)整型域醇。
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)還是可以讀到舊的值。關(guān)于該方法的更多信息可以參考并發(fā)網(wǎng)翻譯的一篇文章《AtomicLong.lazySet是如何工作的蚪腋?》
- int getAndSet(int newValue):以原子方式設(shè)置為newValue的值丰歌,并返回舊值。
AtomicInteger例子代碼如下:
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
餐后甜點(diǎn)
Atomic包提供了三種基本類(lèi)型的原子更新屉凯,但是Java的基本類(lèi)型里還有char立帖,float和double等。那么問(wèn)題來(lái)了悠砚,如何原子的更新其他的基本類(lèi)型呢晓勇?Atomic包里的類(lèi)基本都是使用Unsafe實(shí)現(xiàn)的,讓我們一起看下Unsafe的源碼灌旧,發(fā)現(xiàn)Unsafe只提供了三種CAS方法绑咱,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong枢泰,再看AtomicBoolean源碼描融,發(fā)現(xiàn)其是先把Boolean轉(zhuǎn)換成整型,再使用compareAndSwapInt進(jìn)行CAS衡蚂,所以原子更新double也可以用類(lèi)似的思路來(lái)實(shí)現(xiàn)窿克。
轉(zhuǎn)載自并發(fā)編程網(wǎng) – ifeve.com本文鏈接地址: Java中的Atomic包使用指南
原子更新數(shù)組類(lèi)
通過(guò)原子的方式更新數(shù)組里的某個(gè)元素,Atomic包提供了以下三個(gè)類(lèi):
- AtomicIntegerArray:原子更新整型數(shù)組里的元素毛甲。
- AtomicLongArray:原子更新長(zhǎng)整型數(shù)組里的元素年叮。
- AtomicReferenceArray:原子更新引用類(lèi)型數(shù)組里的元素。
AtomicIntegerArray類(lèi)主要是提供原子的方式更新數(shù)組里的整型玻募,其常用方法如下
- int addAndGet(int i, int delta):以原子方式將輸入值與數(shù)組中索引i的元素相加只损。
- boolean compareAndSet(int i, int expect, int update):如果當(dāng)前值等于預(yù)期值,則以原子方式將數(shù)組位置i的元素設(shè)置成update值七咧。
實(shí)例代碼如下:
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]);
}
}
輸出
3
1
AtomicIntegerArray類(lèi)需要注意的是改执,數(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ù)組衬横。
原子更新引用類(lèi)型
原子更新基本類(lèi)型的AtomicInteger裹粤,只能更新一個(gè)變量,如果要原子的更新多個(gè)變量,就需要使用這個(gè)原子更新引用類(lèi)型提供的類(lèi)遥诉。Atomic包提供了以下三個(gè)類(lèi):
- AtomicReference:原子更新引用類(lèi)型拇泣。
- AtomicReferenceFieldUpdater:原子更新引用類(lèi)型里的字段。
- AtomicMarkableReference:原子更新帶有標(biāo)記位的引用類(lèi)型矮锈∶瓜瑁可以原子的更新一個(gè)布爾類(lèi)型的標(biāo)記位和引用類(lèi)型。構(gòu)造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
AtomicReference的使用例子代碼如下:
public class AtomicReferenceTest {
public static AtomicReference<user> atomicUserRef = new AtomicReference<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;
}
}
}
輸出
Shinichi
17
原子更新字段類(lèi)
如果我們只需要某個(gè)類(lèi)里的某個(gè)字段苞笨,那么就需要使用原子更新字段類(lèi)债朵,Atomic包提供了以下三個(gè)類(lèi):
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFieldUpdater:原子更新長(zhǎng)整型字段的更新器瀑凝。
- AtomicStampedReference:原子更新帶有版本號(hào)的引用類(lèi)型序芦。該類(lèi)將整數(shù)值與引用關(guān)聯(lián)起來(lái),可用于原子的更數(shù)據(jù)和數(shù)據(jù)的版本號(hào)粤咪,可以解決使用CAS進(jìn)行原子更新時(shí)谚中,可能出現(xiàn)的ABA問(wèn)題。
原子更新字段類(lèi)都是抽象類(lèi)寥枝,每次使用都時(shí)候必須使用靜態(tài)方法newUpdater創(chuàng)建一個(gè)更新器宪塔。原子更新類(lèi)的字段的必須使用public volatile修飾符。AtomicIntegerFieldUpdater的例子代碼如下:
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;
}
}
}
輸出
10
11
轉(zhuǎn)載自并發(fā)編程網(wǎng) – ifeve.com本文鏈接地址: Java中的Atomic包使用指南