<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          i++存在什么樣的線程安全問(wèn)題?關(guān)鍵字volatile能解什么問(wèn)題?

          共 2458字,需瀏覽 5分鐘

           ·

          2021-01-27 13:57

          不點(diǎn)藍(lán)字,我們哪來(lái)故事?

          每天 11 點(diǎn)更新文章,餓了點(diǎn)外賣,點(diǎn)擊 ??《無(wú)門檻外賣優(yōu)惠券,每天免費(fèi)領(lǐng)!》


          簡(jiǎn)介

          volatile關(guān)鍵字保證了在多線程環(huán)境下,被修飾的變量在別修改后會(huì)馬上同步到主存,這樣該線程對(duì)這個(gè)變量的修改就是對(duì)所有其他線程可見(jiàn)的,其他線程能夠馬上讀到這個(gè)修改后值.

          Thread的本地內(nèi)存

          • 每個(gè)Thread都擁有自己的線程存儲(chǔ)空間

          • Thread何時(shí)同步本地存儲(chǔ)空間的數(shù)據(jù)到主存是不確定的

          例子

          借用Google?JEREMY MANSON?的解釋,上圖表示兩個(gè)線程并發(fā)執(zhí)行,而且代碼順序上為Thread1->Thread2

          1. 不用 volatile

          假如ready字段不使用volatile,那么Thread 1對(duì)ready做出的修改對(duì)于Thread2來(lái)說(shuō)未必是可見(jiàn)的,是否可見(jiàn)是不確定的.假如此時(shí)thread1 ready泄露了(leak through)了,那么Thread 2可以看見(jiàn)ready為true,但是有可能answer的改變并沒(méi)有泄露,則thread2有可能會(huì)輸出 0 (answer=42對(duì)thread2并不可見(jiàn))

          2. 使用 volatile

          使用volatile以后,做了如下事情

          • 每次修改volatile變量都會(huì)同步到主存中

          • 每次讀取volatile變量的值都強(qiáng)制從主存讀取最新的值(強(qiáng)制JVM不可優(yōu)化volatile變量,如JVM優(yōu)化后變量讀取會(huì)使用cpu緩存而不從主存中讀取)

          • 線程 A 中寫入 volatile 變量之前可見(jiàn)的變量, 在線程 B 中讀取該 volatile 變量以后, 線程 B 對(duì)其他在 A 中的可見(jiàn)變量也可見(jiàn). 換句話說(shuō), 寫 volatile 類似于退出同步塊, 而讀取 volatile 類似于進(jìn)入同步塊

          所以如果使用了volatile,那么Thread2讀取到的值為read=>true,answer=>42,當(dāng)然使用volatile的同時(shí)也會(huì)增加性能開(kāi)銷

          注意

          volatile并不能保證非源自性操作的多線程安全問(wèn)題得到解決,volatile解決的是多線程間共享變量的「可見(jiàn)性」問(wèn)題,而例如多線程的i++,++i,依然還是會(huì)存在多線程問(wèn)題,它是無(wú)法解決了.如下:使用一個(gè)線程i++,另一個(gè)i--,最終得到的結(jié)果不為0

          public?class?VolatileTest?{

          ????private?static?volatile?int?count?=?0;
          ????private?static?final?int?times?=?Integer.MAX_VALUE;

          ????public?static?void?main(String[]?args)?{

          ????????long?curTime?=?System.nanoTime();

          ????????Thread?decThread?=?new?DecThread();
          ????????decThread.start();

          ????????//?使用run()來(lái)運(yùn)行結(jié)果為0,原因是單線程執(zhí)行不會(huì)有線程安全問(wèn)題
          ????????//?new?DecThread().run();

          ????????System.out.println("Start?thread:?"?+?Thread.currentThread()?+?"?i++");

          ????????for?(int?i?=?0;?i?????????????count++;
          ????????}

          ????????System.out.println("End?thread:?"?+?Thread.currentThread()?+?"?i--");

          ????????//?等待decThread結(jié)束
          ????????while?(decThread.isAlive());

          ????????long?duration?=?System.nanoTime()?-?curTime;
          ????????System.out.println("Result:?"?+?count);
          ????????System.out.format("Duration:?%.2fs\n",?duration?/?1.0e9);
          ????}

          ????private?static?class?DecThread?extends?Thread?{

          ????????@Override
          ????????public?void?run()?{
          ????????????System.out.println("Start?thread:?"?+?Thread.currentThread()?+?"?i--");
          ????????????for?(int?i?=?0;?i?????????????????count--;
          ????????????}
          ????????????System.out.println("End?thread:?"?+?Thread.currentThread()?+?"?i--");
          ????????}
          ????}
          }

          最后輸出的結(jié)果是

          Start?thread:?Thread[main,5,main]?i++
          Start?thread:?Thread[Thread-0,5,main]?i--
          End?thread:?Thread[main,5,main]?i--
          End?thread:?Thread[Thread-0,5,main]?i--
          Result:?-460370604
          Duration:?67.37s

          原因是i++和++i并非原子操作,我們?nèi)舨榭醋止?jié)碼,會(huì)發(fā)現(xiàn)

          void?f1()?{?i++;?}

          的字節(jié)碼如下

          void?f1();
          Code:
          0:?aload_0
          1:?dup
          2:?getfield?#2;?//Field?i:I
          5:?iconst_1
          6:?iadd
          7:?putfield?#2;?//Field?i:I
          10:?return

          可見(jiàn)i++執(zhí)行了多部操作, 從變量i中讀取讀取i的值 -> 值+1 -> 將+1后的值寫回i中,這樣在多線程的時(shí)候執(zhí)行情況就類似如下了

          Thread1?????????????Thread2
          r1?=?i;?????????????r3?=?i;???????????????
          r2?=?r1?+?1;????????r4?=?r3?+?1;
          i?=?r2;?????????????i?=?r4;

          這樣會(huì)造成的問(wèn)題就是 r1, r3讀到的值都是 0, 最后兩個(gè)線程都將 1 寫入 i, 最后 i 等于 1, 但是卻進(jìn)行了兩次自增操作

          可知加了volatile和沒(méi)加volatile都無(wú)法解決非原子操作的線程同步問(wèn)題

          線程同步問(wèn)題的解決

          Java提供了java.util.concurrent.atomic 包來(lái)提供線程安全的基本類型包裝類,例子如下

          package?com.qunar.atomicinteger;

          import?java.util.concurrent.atomic.AtomicInteger;

          /**
          ?*?@author?zhenwei.liu?created?on?2013?13-9-2?下午10:18
          ?*?@version?$Id$
          ?*/

          public?class?SafeTest?{

          ????private?static?AtomicInteger?count?=?new?AtomicInteger(0);
          ????private?static?final?int?times?=?Integer.MAX_VALUE;

          ????public?static?void?main(String[]?args)?{

          ????????long?curTime?=?System.nanoTime();

          ????????Thread?decThread?=?new?DecThread();
          ????????decThread.start();

          ????????//?使用run()來(lái)運(yùn)行結(jié)果為0,原因是單線程執(zhí)行不會(huì)有線程安全問(wèn)題
          ????????//?new?DecThread().run();

          ????????System.out.println("Start?thread:?"?+?Thread.currentThread()?+?"?i++");

          ????????for?(int?i?=?0;?i?????????????count.incrementAndGet();
          ????????}

          ????????//?等待decThread結(jié)束
          ????????while?(decThread.isAlive());

          ????????long?duration?=?System.nanoTime()?-?curTime;
          ????????System.out.println("Result:?"?+?count);
          ????????System.out.format("Duration:?%.2f\n",?duration?/?1.0e9);
          ????}

          ????private?static?class?DecThread?extends?Thread?{

          ????????@Override
          ????????public?void?run()?{
          ????????????System.out.println("Start?thread:?"?+?Thread.currentThread()?+?"?i--");
          ????????????for?(int?i?=?0;?i?????????????????count.decrementAndGet();
          ????????????}
          ????????????System.out.println("End?thread:?"?+?Thread.currentThread()?+?"?i--");
          ????????}
          ????}
          }

          輸出

          Start?thread:?Thread[main,5,main]?i++
          Start?thread:?Thread[Thread-0,5,main]?i--
          End?thread:?Thread[Thread-0,5,main]?i--
          Result:?0
          Duration:?105.15

          結(jié)論

          1. volatile解決了線程間共享變量的可見(jiàn)性問(wèn)題

          2. 使用volatile會(huì)增加性能開(kāi)銷

          3. volatile并不能解決線程同步問(wèn)題

          4. 解決i++或者++i這樣的線程同步問(wèn)題需要使用synchronized或者AtomicXX系列的包裝類,同時(shí)也會(huì)增加性能開(kāi)銷


          往期推薦

          誤用一個(gè)雙引號(hào),生產(chǎn)數(shù)據(jù)全變0了!千萬(wàn)不要犯這樣的錯(cuò)誤!

          一次線上 Redis 高負(fù)載排查經(jīng)歷,步步驚心!

          Java 中的通配符 T,E,K,V,?,你確定都了解嗎?

          5分鐘了解 CDN 加速原理

          下方二維碼關(guān)注我

          技術(shù)草根,堅(jiān)持分享?編程,算法,架構(gòu)

          看完文章,餓了點(diǎn)外賣,點(diǎn)擊 ??《無(wú)門檻外賣優(yōu)惠券,每天免費(fèi)領(lǐng)!》

          朋友,助攻一把!點(diǎn)個(gè)在看
          瀏覽 23
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲第一页综合 | 操逼的乐趣| 婷婷影音先锋 | 爽爽网站| 免费看黄色日逼视频 |