Android線程篇(七):多線程下的緩存一致性問題

這篇文章,必須了解Java虛擬機的內存模型和CUP的內存架構扇丛,不了解的同學速度學習前兩篇。

Java內存模型:
Android線程篇(五):Java內存模型
CPU內存架構:
Android線程篇(六):CPU內存架構

繼續(xù)上篇文章的例子,稍作改動:

Int count=0
count=count+1

如果count=count+1在單線程里面運行尉辑,這個是沒有任何問題的帆精,但是在多線程中運行就會有問題,會有什么問題呢?

當線程執(zhí)行count=count+1時會先從主內存讀取count的值隧魄,然后復制一份到CPU的高速緩存中卓练,對count進行+1操作,將count的結果寫入高速緩存中购啄,再將i的值刷新到主內存當中襟企。

如果有倆個線程同時執(zhí)行這個代碼,我們期望的結果為2狮含,到底會出現(xiàn)什么情況呢顽悼?我們繼續(xù)分析。

開始時几迄,倆個線程分別讀取count的值到各自的CPU高速緩存當中蔚龙,線程1和線程2對count進行+1操作,線程1將count的結果寫入高速緩存中映胁,再將i的值刷新到主內存當中木羹,此時線程2高速緩存中,count的值還是0屿愚,進行加1操作之后汇跨,count的值為1务荆,然后線程2把count的值寫入內存,這個時候count的值還為1穷遂。

最終結果i的值是1函匕,而不是2。這就是著名的緩存一致性問題蚪黑。通常稱這種被多個線程訪問的變量為共享變量盅惜。

在多線程編程的時候,如果一個變量在多個CUP中都有緩存忌穿,就可能會出現(xiàn)緩存不一致性問題抒寂。

問題清楚了,我們如何來解決這個問題呢掠剑?

來先上個例子屈芜,至于為什么不用Int請看上篇文章:

文章鏈接:
Java線程并發(fā)小例子的思考,尋求大佬答疑解惑

    public Integer count = 0;
    public int TestVolatile(){
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    count++;
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<"+count);
        return count;
    }

Log:

03-18 03:00:16.098 5569-5569/com.example.myapplication I/System.out: <<<<<863
03-18 03:01:55.414 5569-5569/com.example.myapplication I/System.out: <<<<<1000
03-18 03:01:58.210 5569-5569/com.example.myapplication I/System.out: <<<<<976
03-18 03:02:00.426 5569-5569/com.example.myapplication I/System.out: <<<<<925

我們期望count結果等于1000朴译,結果看log井佑,都是小于1000的,那么我們如何能讓結果等于我們期望的1000呢:
第一種:
采用synchronized:

public Integer count = 0;
    public Integer TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public synchronized void increase() {
        count++;
    }

Log:

03-25 14:20:02.426 15465-15465/? I/System.out: <<<<<1000
03-25 14:20:09.868 15465-15465/com.example.myapplication I/System.out: <<<<<1000
03-25 14:20:13.214 15465-15465/com.example.myapplication I/System.out: <<<<<1000

第二種:采用Lock:

    public Integer count = 0;
    Lock lock = new ReentrantLock();
    public Integer TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public void increase() {
        lock.lock();
        try {
            count++;
        } finally{
            lock.unlock();
        }

    }

這里就不貼Log了眠寿,有興趣的朋友自己試躬翁。。盯拱。

第三種:采用AtomicInteger:

public AtomicInteger count = new AtomicInteger();
    public AtomicInteger TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public void increase() {
        count.getAndIncrement();

    }

至于這幾種方法都有什么優(yōu)劣盒发,他們的原理都有什么,后面的文章我們繼續(xù)講解狡逢。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末宁舰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奢浑,更是在濱河造成了極大的恐慌明吩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件殷费,死亡現(xiàn)場離奇詭異,居然都是意外死亡低葫,警方通過查閱死者的電腦和手機详羡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘿悬,“玉大人实柠,你說我怎么就攤上這事∩普牵” “怎么了窒盐?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵草则,是天一觀的道長。 經常有香客問我蟹漓,道長炕横,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任葡粒,我火速辦了婚禮份殿,結果婚禮上梧疲,老公的妹妹穿的比我還像新娘爹橱。我一直安慰自己地消,他們只是感情好势腮,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布蔓搞。 她就那樣靜靜地躺著扩劝,像睡著了一般缺菌。 火紅的嫁衣襯著肌膚如雪薇溃。 梳的紋絲不亂的頭發(fā)上盒让,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天梅肤,我揣著相機與錄音,去河邊找鬼糯彬。 笑死凭语,一個胖子當著我的面吹牛,可吹牛的內容都是我干的撩扒。 我是一名探鬼主播似扔,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼搓谆!你這毒婦竟也來了炒辉?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泉手,失蹤者是張志新(化名)和其女友劉穎黔寇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斩萌,經...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡缝裤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了颊郎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片憋飞。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姆吭,靈堂內的尸體忽然破棺而出榛做,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布检眯,位于F島的核電站厘擂,受9級特大地震影響,放射性物質發(fā)生泄漏锰瘸。R本人自食惡果不足惜刽严,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望获茬。 院中可真熱鬧港庄,春花似錦、人聲如沸恕曲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佩谣。三九已至把还,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茸俭,已是汗流浹背吊履。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留调鬓,地道東北人艇炎。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像腾窝,于是被迫代替她去往敵國和親缀踪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355