java多線程-06-atomic包

[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包使用指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末囊拜,一起剝皮案震驚了整個(gè)濱河市某筐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艾疟,老刑警劉巖来吩,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蔽莱,居然都是意外死亡弟疆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)盗冷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怠苔,“玉大人,你說(shuō)我怎么就攤上這事仪糖「趟荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锅劝,是天一觀的道長(zhǎng)攒驰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)故爵,這世上最難降的妖魔是什么玻粪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上劲室,老公的妹妹穿的比我還像新娘伦仍。我一直安慰自己,他們只是感情好很洋,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布充蓝。 她就那樣靜靜地躺著,像睡著了一般喉磁。 火紅的嫁衣襯著肌膚如雪谓苟。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天线定,我揣著相機(jī)與錄音娜谊,去河邊找鬼。 笑死斤讥,一個(gè)胖子當(dāng)著我的面吹牛纱皆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播芭商,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼派草,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铛楣?” 一聲冷哼從身側(cè)響起近迁,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎簸州,沒(méi)想到半個(gè)月后鉴竭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岸浑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年搏存,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矢洲。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璧眠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出读虏,到底是詐尸還是另有隱情责静,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布盖桥,位于F島的核電站灾螃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏揩徊。R本人自食惡果不足惜睦焕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一藐握、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧垃喊,春花似錦、人聲如沸袜炕。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)偎窘。三九已至乌助,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陌知,已是汗流浹背他托。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仆葡,地道東北人赏参。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像沿盅,于是被迫代替她去往敵國(guó)和親把篓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

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