Java多線程3 原子性操作類的使用

Java多線程目錄
在java5以后滞详,我們接觸到了線程原子性操作,也就是在修改時(shí)我們只需要保證它的那個(gè)瞬間是安全的即可锐极,經(jīng)過(guò)相應(yīng)的包裝后可以再處理對(duì)象的并發(fā)修改盒齿,本文總結(jié)一下Atomic系列的類的使用方法沿猜,其中包含:

原子更新類型 名稱 描述
基本類型 AtomicBoolean 原子更新布爾類型
基本類型 AtomicInteger 原子更新整型
基本類型 AtomicLong 原子更新長(zhǎng)整型
數(shù)組類型 AtomicIntegerArray 原子更新整型數(shù)組里的元素
數(shù)組類型 AtomicLongArray 原子更新長(zhǎng)整型數(shù)組里的元素
數(shù)組類型 AtomicReferenceArray 原子更新引用類型數(shù)組的元素
數(shù)組類型 AtomicBooleanArray 原子更新布爾類型數(shù)組的元素
引用類型 AtomicReference 原子更新引用類型
引用類型 AtomicReferenceFieldUpdater 原子更新引用類型里的字段
引用類型 AtomicMarkableReference 原子更新帶有標(biāo)記位的引用類型⊥爰梗可以原子更新一個(gè)布爾類型的標(biāo)記位和應(yīng)用類型
字段類型 AtomicIntegerFieldUpdater 原子更新整型的字段的更新器
字段類型 AtomicLongFieldUpdater 原子更新長(zhǎng)整型字段的更新器
字段類型 AtomicStampedReference 原子更新帶有版本號(hào)的引用類型啼肩。該類將整型數(shù)值與引用關(guān)聯(lián)起來(lái),可用于原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào)衙伶,可以解決使用CAS進(jìn)行原子更新時(shí)可能出現(xiàn)的ABA問(wèn)題祈坠。

1. 基本類型的使用

public class AtomicTest {
    /**
     * 常見(jiàn)的方法列表
     *
     * @see AtomicInteger#get()             直接返回值
     * @see AtomicInteger#getAndAdd(int)    增加指定的數(shù)據(jù),返回變化前的數(shù)據(jù)
     * @see AtomicInteger#getAndDecrement() 減少1矢劲,返回減少前的數(shù)據(jù)
     * @see AtomicInteger#getAndIncrement() 增加1赦拘,返回增加前的數(shù)據(jù)
     * @see AtomicInteger#getAndSet(int)    設(shè)置指定的數(shù)據(jù),返回設(shè)置前的數(shù)據(jù)
     * @see AtomicInteger#addAndGet(int)    增加指定的數(shù)據(jù)后返回增加后的數(shù)據(jù)
     * @see AtomicInteger#decrementAndGet() 減少1芬沉,返回減少后的值
     * @see AtomicInteger#incrementAndGet() 增加1躺同,返回增加后的值
     * @see AtomicInteger#lazySet(int)      僅僅當(dāng)get時(shí)才會(huì)set
     * @see AtomicInteger#compareAndSet(int, int) 嘗試新增后對(duì)比,若增加成功則返回true否則返回false
     **/

    public static void main(String[] args) {

        final AtomicTicket ticket = new AtomicTicket();
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (ticket.getCount() > 0) {
                        System.out.println(Thread.currentThread().getName() + " count: " + ticket.decrement());
                    }
                }
            }).start();
        }
    }
}


class AtomicTicket {

    public AtomicInteger count = new AtomicInteger(100);

    public int decrement() {

        return count.getAndDecrement();
    }

    public int getCount() {
        return count.get();

    }
}
Thread-0 count: 100
Thread-2 count: 98
Thread-1 count: 99
Thread-2 count: 96
Thread-0 count: 97
Thread-2 count: 94
Thread-2 count: 92
Thread-1 count: 95
中間省略...
Thread-1 count: 12
Thread-2 count: 7
Thread-0 count: 9
Thread-2 count: 5
Thread-1 count: 6
Thread-2 count: 3
Thread-0 count: 4
Thread-2 count: 1
Thread-1 count: 2

2. 數(shù)組類型的使用

public class AtomicIntegerArrayTest {

    /**
     * 常見(jiàn)的方法列表
     * @see AtomicIntegerArray#addAndGet(int, int) 執(zhí)行加法丸逸,第一個(gè)參數(shù)為數(shù)組的下標(biāo)蹋艺,第二個(gè)參數(shù)為增加的數(shù)量,返回增加后的結(jié)果
     * @see AtomicIntegerArray#compareAndSet(int, int, int) 對(duì)比修改黄刚,參數(shù)1:數(shù)組下標(biāo)捎谨,參數(shù)2:原始值,參數(shù)3憔维,修改目標(biāo)值涛救,修改成功返回true否則false
     * @see AtomicIntegerArray#decrementAndGet(int) 參數(shù)為數(shù)組下標(biāo),將數(shù)組對(duì)應(yīng)數(shù)字減少1业扒,返回減少后的數(shù)據(jù)
     * @see AtomicIntegerArray#incrementAndGet(int) 參數(shù)為數(shù)組下標(biāo)检吆,將數(shù)組對(duì)應(yīng)數(shù)字增加1,返回增加后的數(shù)據(jù)
     *
     * @see AtomicIntegerArray#getAndAdd(int, int) 和addAndGet類似凶赁,區(qū)別是返回值是變化前的數(shù)據(jù)
     * @see AtomicIntegerArray#getAndDecrement(int) 和decrementAndGet類似咧栗,區(qū)別是返回變化前的數(shù)據(jù)
     * @see AtomicIntegerArray#getAndIncrement(int) 和incrementAndGet類似,區(qū)別是返回變化前的數(shù)據(jù)
     * @see AtomicIntegerArray#getAndSet(int, int) 將對(duì)應(yīng)下標(biāo)的數(shù)字設(shè)置為指定值虱肄,第二個(gè)參數(shù)為設(shè)置的值致板,返回是變化前的數(shù)據(jù)
     */
    private final static AtomicIntegerArray ATOMIC_INTEGER_ARRAY = new AtomicIntegerArray(new int[]{1,2,3,4,5,6,7,8,9,10});

    public static void main(String []args) throws InterruptedException {
        Thread []threads = new Thread[10];
        for(int i = 0 ; i < 10 ; i++) {
            final int index = i;
            threads[i] = new Thread() {
                public void run() {
                    int original =  ATOMIC_INTEGER_ARRAY.get(index);
                    int result = ATOMIC_INTEGER_ARRAY.addAndGet(index, index + 1);
                    System.out.println("currentThread:" + Thread.currentThread().getName() + " , 原始值為:" + original + ",增加后的結(jié)果為:" + result);
                }
            };
            threads[i].start();
        }
        for(Thread thread : threads) {
            thread.join();
        }
        System.out.println("=========================>\n執(zhí)行已經(jīng)完成咏窿,結(jié)果列表:");
        for(int i = 0 ; i < ATOMIC_INTEGER_ARRAY.length() ; i++) {
            System.out.println(ATOMIC_INTEGER_ARRAY.get(i));
        }
    }
}

currentThread:Thread-0 , 原始值為:1斟或,增加后的結(jié)果為:2
currentThread:Thread-3 , 原始值為:4,增加后的結(jié)果為:8
currentThread:Thread-2 , 原始值為:3集嵌,增加后的結(jié)果為:6
currentThread:Thread-1 , 原始值為:2萝挤,增加后的結(jié)果為:4
currentThread:Thread-5 , 原始值為:6御毅,增加后的結(jié)果為:12
currentThread:Thread-4 , 原始值為:5,增加后的結(jié)果為:10
currentThread:Thread-6 , 原始值為:7怜珍,增加后的結(jié)果為:14
currentThread:Thread-7 , 原始值為:8端蛆,增加后的結(jié)果為:16
currentThread:Thread-8 , 原始值為:9,增加后的結(jié)果為:18
currentThread:Thread-9 , 原始值為:10酥泛,增加后的結(jié)果為:20
=========================>
執(zhí)行已經(jīng)完成今豆,結(jié)果列表:
2
4
6
8
10
12
14
16
18
20

3. 引用類型的使用

public class AtomicReferenceTest {

    public static void main(String[] args) {
        People people1 =new People("Bom", 0);
        People people2 =new People("Tom",10);

        //先初始化一個(gè)值,如果不初始化則默認(rèn)值為null
        AtomicReference<People> reference = new AtomicReference<>(people1);
        People people3 = reference.get();
        if (people3.equals(people1)) {
            System.out.println("people3:" + people3);
        } else {
            System.out.println("else:" + people3);
        }

        /**
         * 當(dāng)前值:拿當(dāng)前值和reference.get()獲取到的值去比較柔袁,如果相等則true并更新值為期望值
         * 期望值:如果返回true則更新為期望值呆躲,如果返回false則不更新值
         */
        boolean b = reference.compareAndSet(null, people2);
        System.out.println("myClass.main-"+b+"--"+reference.get());

        boolean b1 = reference.compareAndSet(people1, people2);
        System.out.println("myClass.main-"+b1+"--"+reference.get());


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());

                People people = reference.get();
                people.setName("Tom"+Thread.currentThread().getName());
                people.setAge(people.getAge()+1);
                reference.getAndSet(people);
                System.out.println(Thread.currentThread().getName()+reference.get().toString());
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());

                People people = reference.get();
                people.setName("Tom"+Thread.currentThread().getName());
                people.setAge(people.getAge()+4);
                reference.getAndSet(people);
                System.out.println(Thread.currentThread().getName()+reference.get().toString());
            }
        }).start();

    }

}

 class People {
    private String name;
    private int age;

    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

4.字段類型的使用

public class AtomicIntegerFieldUpdaterTest {

    /**
     * 可以直接訪問(wèn)對(duì)應(yīng)的變量,進(jìn)行修改和處理
     * 條件:要在可訪問(wèn)的區(qū)域內(nèi)捶索,如果是private或挎包訪問(wèn)default類型以及非父親類的protected均無(wú)法訪問(wèn)到
     * 其次訪問(wèn)對(duì)象不能是static類型的變量(因?yàn)樵谟?jì)算屬性的偏移量的時(shí)候無(wú)法計(jì)算)插掂,也不能是final類型的變量(因?yàn)楦緹o(wú)法修改),必須是普通的成員變量
     * <p>
     * 方法(說(shuō)明上和AtomicInteger幾乎一致腥例,唯一的區(qū)別是第一個(gè)參數(shù)需要傳入對(duì)象的引用)
     *
     * @see AtomicIntegerFieldUpdater#addAndGet(Object, int)
     * @see AtomicIntegerFieldUpdater#compareAndSet(Object, int, int)
     * @see AtomicIntegerFieldUpdater#decrementAndGet(Object)
     * @see AtomicIntegerFieldUpdater#incrementAndGet(Object)
     * @see AtomicIntegerFieldUpdater#getAndAdd(Object, int)
     * @see AtomicIntegerFieldUpdater#getAndDecrement(Object)
     * @see AtomicIntegerFieldUpdater#getAndIncrement(Object)
     * @see AtomicIntegerFieldUpdater#getAndSet(Object, int)
     */
    public final static AtomicIntegerFieldUpdater<A> ATOMIC_INTEGER_UPDATER = AtomicIntegerFieldUpdater.newUpdater(A.class, "intValue");

    public static void main(String[] args) {
        final A a = new A();
        for (int i = 0; i < 10; i++) {

            new Thread() {
                public void run() {
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                    ATOMIC_INTEGER_UPDATER.addAndGet(a, 11);
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                    if (ATOMIC_INTEGER_UPDATER.compareAndSet(a, ATOMIC_INTEGER_UPDATER.get(a), 120)) {
                        System.out.println(Thread.currentThread().getName() + " 對(duì)應(yīng)的值做了修改辅甥!");
                    }
                    System.out.println(
                            Thread.currentThread().getName() + " " + ATOMIC_INTEGER_UPDATER.get(a));
                }
            }.start();
        }
    }

    static class A {
        volatile int intValue = 100;
    }
}

Thread-0 100
Thread-2 100
Thread-1 100
Thread-2 122
Thread-3 111
Thread-5 120
Thread-0 111
Thread-5 142
Thread-3 131
Thread-2 對(duì)應(yīng)的值做了修改!
Thread-2 120
Thread-8 120
Thread-4 133
Thread-1 133
Thread-9 142
Thread-4 142
Thread-4 對(duì)應(yīng)的值做了修改院崇!
Thread-8 131
Thread-3 對(duì)應(yīng)的值做了修改肆氓!
Thread-3 120
Thread-7 120
Thread-7 131
Thread-5 對(duì)應(yīng)的值做了修改!
Thread-5 120
Thread-6 120
Thread-0 對(duì)應(yīng)的值做了修改底瓣!
Thread-6 131
Thread-7 對(duì)應(yīng)的值做了修改谢揪!
Thread-8 對(duì)應(yīng)的值做了修改!
Thread-4 120
Thread-9 131
Thread-1 對(duì)應(yīng)的值做了修改捐凭!
Thread-9 對(duì)應(yīng)的值做了修改拨扶!
Thread-8 120
Thread-7 120
Thread-6 對(duì)應(yīng)的值做了修改!
Thread-0 131
Thread-6 120
Thread-9 120
Thread-1 120
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茁肠,一起剝皮案震驚了整個(gè)濱河市患民,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垦梆,老刑警劉巖匹颤,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異托猩,居然都是意外死亡印蓖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門京腥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赦肃,“玉大人,你說(shuō)我怎么就攤上這事∷穑” “怎么了船侧?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)厅各。 經(jīng)常有香客問(wèn)我镜撩,道長(zhǎng),這世上最難降的妖魔是什么队塘? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任琐鲁,我火速辦了婚禮,結(jié)果婚禮上人灼,老公的妹妹穿的比我還像新娘。我一直安慰自己顾翼,他們只是感情好投放,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著适贸,像睡著了一般灸芳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拜姿,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天烙样,我揣著相機(jī)與錄音,去河邊找鬼蕊肥。 笑死谒获,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的壁却。 我是一名探鬼主播批狱,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼展东!你這毒婦竟也來(lái)了赔硫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盐肃,失蹤者是張志新(化名)和其女友劉穎爪膊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砸王,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡推盛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了处硬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片小槐。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凿跳,到底是詐尸還是另有隱情件豌,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布控嗜,位于F島的核電站茧彤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏疆栏。R本人自食惡果不足惜曾掂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壁顶。 院中可真熱鬧珠洗,春花似錦、人聲如沸若专。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)调衰。三九已至膊爪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嚎莉,已是汗流浹背米酬。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趋箩,地道東北人赃额。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叫确,于是被迫代替她去往敵國(guó)和親爬早。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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