Java-CAS

原子性操作

原子即為不可再分的,原子操作即要么所有操作全部完成 要么全不完成飘庄。
用synchronized包圍的代碼塊或方法就是原子操作脑蠕。對于線程來講,synchronized包圍代碼,只會全部完成谴仙,不會執(zhí)行一半而中斷迂求。
synchronized是一個很重的操作,如果執(zhí)行代碼很簡單晃跺,例如i++,很多線程都阻塞在外面很劃不來揩局,為了解決這種問題,引出了CAS(Compare And Swap)掀虎,比較并且交換凌盯。系統(tǒng)提供了很多原子變量,Atomic開頭的變量都是實現(xiàn)了CAS烹玉,例如AtomicBoolean驰怎、AtomicInteger等。

CAS

CAS原理

我們下面舉個例子:
假如我們現(xiàn)在有多個線程執(zhí)行count++這個操作二打。

CASCount.jpg
  1. 首先從內(nèi)存中取出count的值县忌。(假如這時是0)
  2. 然后進行累加操作。(count變?yōu)榱?)
  3. 這時在從內(nèi)存中取出count的值继效,如果與第一步取到的值相等症杏,則將累加操作后的值寫入內(nèi)存,否則 說明有別的線程改過了瑞信,這時再重復(fù)第一步操作厉颤,直到完成賦值操作。

總結(jié):
獲取內(nèi)存中的值 喧伞,進行操作走芋,再寫入內(nèi)存的時候,進行判斷當(dāng)前內(nèi)存中的值是否與之前取出的值是否一致潘鲫,不一致的話以內(nèi)存中的新值翁逞,重新計算,反復(fù)執(zhí)行(自旋溉仑,其實就是死循環(huán))挖函,直到內(nèi)存中的值沒有在經(jīng)過修改,才進行寫入操作浊竟。

這里就引出了兩個概念:

  • 悲觀鎖 先鎖再操作(synchronized)
  • 樂觀鎖 先操作再判斷是否進行修改

CAS和synchronized性能比較:
正常生產(chǎn)環(huán)境下CAS的效率是要高于synchronized怨喘,因為synchronized會阻塞線程,線程阻塞的時候會發(fā)生上下文切換(3-5ms)振定,CAS執(zhí)行指令的時間大概在0.6ns必怜。
高度競爭,特意設(shè)計的情況下synchronized會優(yōu)于CAS后频。

為什么有CAS還需要synchronized

  • ABA問題:
    假設(shè)當(dāng)前有兩個線程ThreadA和ThreadB梳庆,一個變量A
    ThreadA想把A修改為B暖途,根據(jù)上面介紹的CAS原理,我們知道膏执,修改的時候會判斷A是否被修改過驻售,用段偽代碼表示也就是if(A==A) A = B
    ThreadB比ThreadA跑的快,ThreadA把A修改為C更米,然后又改回為A欺栗。
    可是ThreadA認為A沒被修改過。

解決辦法:版本戳 要求每個線程修改值的時候 加入一個版本戳
jdk中提供了相關(guān)的操作
AtomicMarkReference(有沒有變過) AtomicStampedReference(變了幾次)

public class UserAtomicMarkable {
    static AtomicStampedReference<String> atomicStampedReference = new AtomicStampedReference<>("red",0);

    public static void main(String[] args) throws InterruptedException {
        final int oldStamp = atomicStampedReference.getStamp();
        final String oldReference = atomicStampedReference.getReference();
        System.out.println(oldReference + "----------" + oldStamp);

         Thread  threadA = new Thread(){
            @Override
            public void run() {
                super.run();
                System.out.println(Thread.currentThread().getName() + "當(dāng)前變量值 " + oldReference + "-當(dāng)前版本戳 " + oldStamp );
                atomicStampedReference.compareAndSet(oldReference,oldReference + "Java",oldStamp,oldStamp + 1);
            }
        };

        Thread  threadB = new Thread(){
            @Override
            public void run() {
                super.run();
                String reference = atomicStampedReference.getReference();
                int stamp = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName() + "當(dāng)前變量值 " + reference + "-當(dāng)前版本戳 " + stamp );
                atomicStampedReference.compareAndSet(reference,reference + "C",stamp,stamp + 1);
                String reference1 = atomicStampedReference.getReference();
                int stamp1 = atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName() + "當(dāng)前變量值 " + reference1 + "-當(dāng)前版本戳 " + stamp1 );
            }
        };

        threadA.start();
        threadA.join();
        threadB.start();
        threadB.join();

        System.out.println(atomicStampedReference.getReference() + "----------" + atomicStampedReference.getStamp());
    }

}
  • 開銷問題
    當(dāng)競爭激烈的時候征峦,會存在長時間完成不了操作 迟几,造成自旋,一直重試眶痰,會占用CPU資源瘤旨。
    解決辦法:換成synchronized梯啤。

  • 只能保證一個共享變量的原子操作
    CAS操作時竖伯,只能針對某個內(nèi)存地址上的值進行修改,而一個地址往往只能保存一個變量因宇。
    解決辦法:AtomicReference把多個變量打包到一個對象中 替換對象

public class UseAtomicReference {
    static AtomicReference<User> atomicReference;

    public static void main(String[] args) {
        User user = new User("xiaoming",22);
        atomicReference = new AtomicReference<>(user);
        User updateUser = new User("xiaohong",16);
        atomicReference.compareAndSet(user,updateUser);

        System.out.println(atomicReference.get());
        System.out.println(user);
    }

    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;
        }

        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末七婴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子察滑,更是在濱河造成了極大的恐慌打厘,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贺辰,死亡現(xiàn)場離奇詭異户盯,居然都是意外死亡,警方通過查閱死者的電腦和手機饲化,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門莽鸭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吃靠,你說我怎么就攤上這事硫眨。” “怎么了巢块?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵礁阁,是天一觀的道長。 經(jīng)常有香客問我族奢,道長姥闭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任越走,我火速辦了婚禮棚品,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己南片,他們只是感情好掺涛,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疼进,像睡著了一般薪缆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伞广,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天拣帽,我揣著相機與錄音,去河邊找鬼嚼锄。 笑死减拭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的区丑。 我是一名探鬼主播拧粪,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沧侥!你這毒婦竟也來了可霎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤宴杀,失蹤者是張志新(化名)和其女友劉穎癣朗,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旺罢,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡旷余,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扁达。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片正卧。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罩驻,靈堂內(nèi)的尸體忽然破棺而出穗酥,到底是詐尸還是另有隱情,我是刑警寧澤惠遏,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布砾跃,位于F島的核電站,受9級特大地震影響节吮,放射性物質(zhì)發(fā)生泄漏抽高。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一透绩、第九天 我趴在偏房一處隱蔽的房頂上張望翘骂。 院中可真熱鬧壁熄,春花似錦、人聲如沸碳竟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽莹桅。三九已至昌执,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诈泼,已是汗流浹背懂拾。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铐达,地道東北人岖赋。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像瓮孙,于是被迫代替她去往敵國和親唐断。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351