欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Java線程安全兼談DCL

系統 2122 0

轉載自 ---- http://www.iteye.com/topic/875420

????? 如果你搜索網上分析dcl為什么在java中失效的原因,都會談到編譯器會做優化云云,我相信大家看到這個一定會覺得很沮喪、很無助,對自己寫的 程序很沒信心。我很理解這種感受,因為我也經歷過,這或許是為什么網上一直有人喜歡談dcl的原因。如果放在java5之前,從編譯器的角度去解釋dcl 也無可厚非,在java5的JMM(內存模型)已經得到很大的修正,如果到現在還只能從編譯器的角度去解釋dcl,那簡直就在污辱java,要知道 java的最大優勢就是只需要考慮一個平臺。你可以完全無視網上絕大多數關于dcl的討論,很多時候他們自己都說不清楚,除Doug Lea等幾個大牛,我不相信誰比誰更權威。

很多人不理解dcl,不是dcl有多么復雜,恰恰相反,而是對基礎掌握得不夠。所以,我會先從基礎講起,然后再分析DCL。

我們都知道,當兩個線程同時讀寫(或同時寫)一個共享變量時會發生數據競爭。那我怎么才能知道發生了數據競爭呢?我需要去讀取那個變量,發生數據 競爭通常有兩個表現:一是讀取到陳舊數據,即讀取到雖是曾經寫入的數據,但不是最新的。二是讀取到之前根本沒有寫入的值,也就是說讀到垃圾。

數據陳舊性

為了讀取到另一個線程寫入的最新數據,JMM定義了一系列的規則,最基本的規則就是要利用同步。在Java中,同步的手段有synchronized和volatile兩種,這里我只會涉及到syncrhonized。請大家先記住以下規則,接下來我會細講。

規則一:必須對變量的所有寫和所有讀同步,才能讀取到該最新的數據。

先看下面的代碼:

Java代碼 ? 收藏代碼
  1. public ? class ?A?{??
  2. ???? private ? int ?some;??
  3. ???? public ? int ?another;??
  4. ??
  5. ???? public ? int ?getSome()?{? return ?some;?}??
  6. ???? public ? synchronized ? int ?getSomeWithSync()?{? return ?some;?}??
  7. ???? public ? void ?setSome( int ?v)?{?some?=?v;?}??
  8. ???? public ? synchronized ? void ?setSomeWithSync( int ?v)?{?some?=?v;?}??
  9. }??



讓我們來分析一個線程寫,另一個線程讀的情形,一共四種情形。初始情況都是a = new A(),暫不考慮其它線程。

情形一:讀寫都不同步。

Thread1 Thread2
(1) a.setSome(13)
(2) a.getSome()



Java線程安全兼談DCL

這種情況下,即使thread1先寫入some為13,thread2再讀取some,它能讀到13嗎?在沒有同步協調下,結果是不確定的。從圖 上看出,兩個線程獨立運行,JMM并不保證一個線程能夠看到另一個線程寫入的值。在這個例子中,就是thread2可能讀到0(即some的初始值)而不 是13。注意,在理論上,即使thread2在thread1寫入some之后再等上一萬年也還是可能讀到some的初始值0,盡管這在實際幾乎不可能發 生。


情形二:寫同步,讀不同步

Thread1 Thread2
(1) a.setSomeWithSync(13)
(2) a.getSome()



Java線程安全兼談DCL

情形三:讀同步,寫不同步

Thread1 Thread2
(1) a.setSome(13)
(2) a.getSomeWithSync()



Java線程安全兼談DCL

在這兩種情況下,thread1和thread2只對讀或只對寫some加了鎖,這不起任何作用,和[情形一]一樣,thread2仍有可能讀到 some的初始值0。從圖上也可看出,thread1和thread2互相之間并沒有任何影響,一方加鎖并不影響另一方的繼續運行。圖中也顯示,同步操作 相當于在同步開始執行lock操作,在同步結束時執行unlock操作。

情形四:讀寫都同步

Thread1 Thread2
(1) a.setSomeWithSync(13)
(2) a.getSomeWithSync()



Java線程安全兼談DCL

在情形四中,thread1寫入some時,thread2等待thread1寫入完成,并且它能看到thread1對some做的修改,這時 thread2保證能讀到13。實際上,thread2不僅能看到thread1對some的修改,而且還能看到thread1在修改some之前所做的 任何修改。說得更精確一些,就是一個線程的lock操作能看見另一線程對同一個對象unlock操作之前的所有修改,請注意圖中的紅色箭頭。 沿著圖中箭頭指示方向,箭頭結尾處總能看到箭頭開始處操作做的修改。這樣,a.some[thread2]能看見 lock[thread2],lock[thread2]能看見unlock[thread1],unlock[thread1]又能看見 a.some=13[thread1],即能看到some的值為13。

再來看一個稍微復雜一點的例子:

例子五

Thread1 Thread2
(1) a.another = 5
(2) a.setSomeWithSync(13)
(3) a.getSomeWithSync()
(4) a.another = 7
(5) a.another



Java線程安全兼談DCL

thread2最后會讀到another的什么值呢?會不會讀到another的初始值0呢,畢竟所有對another的訪問都沒有同步?不會。 從圖中很清晰地可以看出,thread2的another至少到看到thread1在lock之前寫入的5,卻并不能保證它能看到thread1在 unlock寫入的7。因此,thread2可以什么讀到another的值可能5或7,但不會是0。你或許已經發現,如果去掉圖中thread2讀取 a.some的操作,這時相當于一個空的同步塊,對結論并沒有任何影響。這說明空的同步塊是起作用的,編譯器不能擅自將空的同步塊優化掉,但你在使用空的 同步塊應該特別小心,通常它都不是你想要的結果。另外需要注意,unlock操作和lock操作必須針對同一個對象,才能保證unlock操作能看到 lock操作之前所做的修改。

例子六:不同的鎖

Java代碼 ? 收藏代碼
  1. class ?B?{??
  2. ???? private ?Object?lock1?=? new ?Object();??
  3. ???? private ?Object?lock2?=? new ?Object();??
  4. ??
  5. ???? private ? int ?some;??
  6. ??
  7. ???? public ? int ?getSome()?{??
  8. ???????? synchronized (lock1)?{? return ?some;?}??
  9. ????}??
  10. ??
  11. ???? public ? void ?setSome( int ?v)?{??
  12. ???????? synchronized (lock2)?{?some?=?v;?}??
  13. ????}??
  14. }??

?

Thread1 Thread2
(1) b.setSome(13)
(2) b.getSome()



Java線程安全兼談DCL

在這種情況下,雖然getSome和setSome都加了鎖,但由于它們是不同的鎖,一個線程運行時并不能阻塞另一個線程運行。因此這里的情形和情形一、二、三一樣,thread2不保證讀到thread1寫入的some最新值。

現在來看DCL:

例子七: DCL

Java代碼 ? 收藏代碼
  1. public ? class ?LazySingleton?{??
  2. ???? private ? int ?someField;??
  3. ??????
  4. ???? private ? static ?LazySingleton?instance;??
  5. ??????
  6. ???? private ?LazySingleton()?{??
  7. ???????? this .someField?=? 201 ;????????????????????????????????? //?(1) ??
  8. ????}??
  9. ??????
  10. ???? public ? static ?LazySingleton?getInstance()?{??
  11. ???????? if ?(instance?==? null )?{??????????????????????????????? //?(2) ??
  12. ???????????? synchronized (LazySingleton. class )?{??????????????? //?(3) ??
  13. ???????????????? if ?(instance?==? null )?{??????????????????????? //?(4) ??
  14. ????????????????????instance?=? new ?LazySingleton();??????????? //?(5) ??
  15. ????????????????}??
  16. ????????????}??
  17. ????????}??
  18. ???????? return ?instance;?????????????????????????????????????? //?(6) ??
  19. ????}??
  20. ??????
  21. ???? public ? int ?getSomeField()?{??
  22. ???????? return ? this .someField;???????????????????????????????? //?(7) ??
  23. ????}??
  24. }??



假設thread1先調用getInstance(),由于此時還沒有任何線程創建LazySingleton實例,它會創建一個實例s并返回。 這是thread2再調用getInstance(),當它運行到(2)處,由于這時讀instance沒有同步,它有可能讀到s或者null(參考情形 二)。先考慮它讀到s的情形,畫出流程圖就是下面這樣的:

Java線程安全兼談DCL

由于thread2已經讀到s,所以getInstance()會立即返回s,這是沒有任何問題,但當它讀取s.someFiled時問題就發生 了。 從圖中可以看thread2沒有任何同步,所以它可能看不到thread1寫入someField的值20,對thread2來說,它可能讀到 s.someField為0,這就是DCL的根本問題。從上面的分析也可以看出,為什么試圖修正DCL但又希望完全避免同步的方法幾乎總是行不通的。

接下來考慮thread2在(2)處讀到instance為null的情形,畫出流程圖:

Java線程安全兼談DCL

接下來thread2會在有鎖的情況下讀取instance的值,這時它保證能讀到s,理由參考情形四或者通過圖中箭頭指示方向來判定。

關于DCL就說這么多,留下兩個問題:

  1. 接著考慮thread2在(2)讀到instance為null的情形,它接著調用s.someFiled會得到什么?會得到0嗎?
  2. DCL為什么要double check,能不能去掉(4)處的check?若不能,為什么?



原子性
回到情形一,為什么我們說thread2讀到some的值只可能為為0或13,而不可能為其它?這是由java對int、引用讀寫都是原子性所決 定的。所謂“原子性”,就是不可分割的最小單元,有數據庫事務概念的同學們應該對此容易理解。當調用some=13時,要么就寫入成功要么就寫入失敗,不 可能寫入一半。但是,java對double, long的讀寫卻不是原子操作,這意味著可能發生某些極端意外的情況??蠢樱?

Java代碼 ? 收藏代碼
  1. public ? class ?C?{??
  2. ???? private ? /*?volatile?*/ ? long ?x;??????????????????????????? //?(1) ??
  3. ??
  4. ???? public ? void ?setX( long ?v)?{?x?=?v;?}??
  5. ???? public ? long ?getX()?{? return ?x;?}??
  6. }??

?

Thread1 Thread2
(1) c.setX(0x1122334400112233L)
(2) c.getX()



thread2讀取x的值可能為0,1122334400112233外,還可能為別的完全意想不到的值。一種情況假設jvm對long的寫入是 先寫低4字節,再寫高4字節,那么讀取到x的值還可能為112233。但是我們不對jvm做如此假設,為了保證對long或double的讀寫是原子操 作,有兩種方式,一是使用volatile,二是使用synchronized。對上面的例子,如果取消(1)處的volatile注釋,將能保證 thread2讀取到x的值要么為0,要么為1122334400112233。如果使用同步,則必須像下面這樣對getX,setX都同步:

Java代碼 ? 收藏代碼
  1. public ? class ?C?{??
  2. ???? private ? /*?volatile?*/ ? long ?x;??????????????????????????? //?(1) ??
  3. ??
  4. ???? public ? synchronized ? void ?setX( long ?v)?{?x?=?v;?}??
  5. ???? public ? synchronized ? long ?getX()?{? return ?x;?}??
  6. }??



因此對原子性也有規則(volatile其實也是一種同步)。

規則二:對double, long變量,只有對所有讀寫都同步,才能保證它的原子性

有時候我們需要保證一個復合操作的原子性,這時就只能使用synchronized。

Java代碼 ? 收藏代碼
  1. public ? class ?Canvas?{??
  2. ???? private ? int ?curX,?curY;??
  3. ??
  4. ???? public ? /*?synchronized?*/ ?getPos()?{??
  5. ???????? return ? new ? int []?{?curX,?curY?};??
  6. ??????????
  7. ????}??
  8. ??
  9. ???? public ? /*?synchronized?*/ ? void ?moveTo( int ?x,? int ?y)?{??
  10. ????????curX?=?x;??
  11. ????????curY?=?y;??
  12. ????}??
  13. }??

?

Thread1 Thread2
(1) c.moveTo(1, 1)
(2) c.moveTo(2, 2)
(3) c.getPos()



當沒有同步的情況下,thread2的getPos可能會得到[1, 2], 盡管該點可能從來沒有出現過。之所以會出現這樣的結果,是因為thread2在調用getPos()時,curX有0,1或2三種可能,同樣curY也有 0,1或2三種可能,所以getPos()可能返回[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]共九種可能。要避免這種情況,只有將getPos()和moveTo都設為同步方法。

總結
以上分析了數據競爭的兩種癥狀,陳舊數據和非原子操作,都是由于沒有恰當同步引起的。這些其實都是相當基礎的知識,同步可有兩種效果:一是保證讀 取最新數據,二是保證操作原子性,但是大多數書籍都對后者過份強調,對前者認識不足,以致對多線程的認識上存在很多誤區。如果想要掌握java線程高級知 識,我只推薦《Java并發編程設計原則與模式》。其實我已經好久沒有寫Java了,這些東西都是我兩年前的知識,如果存在問題,歡迎大家指出,千萬不要 客氣。

Java線程安全兼談DCL


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!??!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 天天操夜夜艹 | 成人国产在线观看 | 色香婷婷 | 国产成年网站v片在线观看 中文字幕在线免费视频 | 国产成人一区二区三区久久久 | 中文字幕在线一区二区三区 | 91久久国产精品 | 美国一级欧美三级 | 91在线品视觉盛宴免费 | 国产亚洲精品久久久久久久久动漫 | 国产精品一码二码三码在线 | 色玖玖综合 | 欧美a区 | 日本最新中文字幕 | www.久久99| 国产成人精品一区二区三区四区 | 日日麻批的全部过程 | 人人欧美| 山岸逢花在线观看无删减 | 久草精品视频在线观看 | 草莓福利视频 | 少妇特黄A片一区二区三区免费看 | 亚州精品天堂中文字幕 | 美女污直播| 99亚洲精品| 香蕉福利久久福利久久香蕉 | 在线观看日韩中文字幕 | 九久精品| 国产伦精品一区二区三区高清 | 国产成人一区二区三区 | 天天操欧美 | 小明永久成人一区二区 | 成人午夜在线 | 亚洲精品无码成人A片在线虐 | 精品日韩在线观看 | 日本妇人成熟A片一区-老狼 | 精品国产91 | 黄在线免费看 | 国产福利在线视频 | 男女污污无遮挡免费观看 | 亚洲激情小视频 |