java 雙重檢查加鎖弊端

http://blog.csdn.net/axman/article/details/1089196

Java是在語言級提供對線程的支持,所以Java的內存模型分為主存儲器和工作存儲器.

[Main?memory]主存儲器就是實例所在的存儲區域,所有實例本身都被放在主存儲器中,當然這
句話本身就說明了實例的字段也在主存儲器中,主存儲器被實例的所有線程所共有.

[working?memory]?工作存儲器當然就是每個線程所專有的工作區域,當然其中有它們共有的
主存儲器中的一些必要的如實例字段等數據的COPY

當然你千萬要知道Java是運行在虛擬系統上的,我們說的主存儲器和工作存儲器都是在物理內存
中虛擬出來的.是在JVM內部而言的.

在JLS中,對字段的存取被規定為read/write,use/assign,lock/unlock?六個最小的操作(action)

對于取而言,當一個線程一次訪問實例字段時,首先從主存儲器復制該字段的值到工作存儲器,
然后線程引用該值.

但是如果同一線程多次訪問該字段,是否每次都是從主存儲器復制到工作存儲器再引用,這由具體
的jvm環境決定.

簡單說有可能不從主存儲器中復制而直接引用工作區的COPY

同樣對于存,如果一個線程一次賦值實例字段,那么會在工作存儲器中進行,然后由jvm決定什么時
候映射到主存儲器.
而同一個線程如果多次反復對實例字段賦值,那么有可能只對工作存儲器的COPY進行,只把最后的
結果同步到主存儲器,當然也有可能是每次都把工作存儲器和主存儲器同步的.這也由具體的JVM決
定.

所以如果多個線程反復對同一實例字段存取,就有可能一個線程對這個字段的改變沒有及時反映給
其它線程,因為至少必須被從工作存儲器同步到主存儲器才能其它線程知道,而在線程專有的工作
存儲器中的值其它線程是不可訪問的.

所以boolean?isInterrupted?=?false;這一句有可能一個線程中斷后在這個線程工作的范圍內已經
設為true,但還沒有立即被映射到主存儲器時,其它線程還不知道.

同樣,對于double?check,我們來看它的問題:
在java與模式一書中,作者這對個問題的說明根本沒有正確地理解.而且他一再說明是錯誤的例子
其實是正確的.

public?MyObject{
????private?static?MyObect?obj;
????public?static?getInstance(){
????????if(obj?==?null){
????????????synchronized(MyObj.class){
????????????????if(obj?==?null)
????????????????????obj?=?new?MyObject();
????????????}
????????}
????????return?obj;
????}
}

這個例子根本不會存在問題.因為線程的同步保證了不會在同步塊外部發生多線程調,而在同步塊中
只有一個線程能執行,其賦值的結果在離開同步塊時會強制映射到主工作區,也就是對obj的賦值一定
會在主工作區反映出來.所以作者舉這個例子說明他根本沒有理解為什么double?check是不安全的.

我們來看下面的例子:
public?MyObject{
????private?static?MyObect?obj;
????private?Date?d?=?new?Data();
????public?Data?getD(){return?this.d;}
????public?static?MyObect? getInstance(){
????????if(obj?==?null){
????????????synchronized(MyObect?.class){
????????????????if(obj?==?null)
????????????????????obj?=?new?MyObject();//這里
????????????}
????????}
????????return?obj;
????}
}
一個線程A運行到"這里"時,對于A的工作區中,肯定已經產生一個MyObect對象,而且這時這個對象已經
完成了Data?d.現在線程A調用時間到,執行權被切換到另一個線程B來執行,會有什么問題呢?

如果obj不為null,線程B獲得了一個obj,但可能obj.getD()卻還沒有初始化.

為什么?既然obj已經可見了(線程A還沒有離開同步塊),而d卻不可見呢?
如果d不可見,那么obj也應該為null啊?線程B應該等待A釋放同步塊啊?

事實上,對于"這里"這條語句,線程A還沒有離開同步塊.
因為沒有"離開同步塊"這個條件,線程a的工作區沒有強制與主存儲器同步,這時工作區中有兩個字段
obj,d?到底先把誰同步到主存儲區,沒有條件限制,雖然在線程A的工作區obj和d都是完整的,但有JSL
沒有強制不允許先把obj映射到主存儲區,如果哪個jvm實現按它的優化方案先把工作存儲器中的obj
同步到主存儲器了,這時正好線程B獲取了,而d卻沒有同步過去,那么線程B就獲取了obj的引用卻找不能
obj.getD();

這個問題是否真的會發生?

我個人的意見,從理論上是會發生的,所以如果是設計銀行交易系統你就沒有必要為提高一些性能而放
棄安全性,而如果只是一般的應用,發生這種情況的機率本來就非常發生也不會有太多的危害,我認為
可以不去介意.比如獲取數據庫連結,即使獲取不到也不過讓訪問者再刷新一次重新查詢而已.事實上有
可能幾天,幾個月,一年都不會發生一次.知道小行星有可能會撞地球的,但你不要太在意而影響你正常
的生活.如果你不是做銀行交易系統的你不要太擔心,而大多數JVM實現本身就會保證這種安全的.也就?
在把obj同步到主存儲器會先同步d,但萬一發生相反的順序,你就無權責備你所用的JVM.只是這種萬一
機會不是很多.


當然如果是貪婪式調用,靜態實例的初始化由ClassLoader經由[Thread?Safe]來完成,當然就不會有這
個問題了:
public?MyObject{
????private?static?MyObect?obj?=?new?MyObject();
????private?Date?d?=?new?Data();
????public?Data?getD(){return?this.d;}
????public?static?getInstance(){
????????return?obj;
????}
}


那么如何保證實例字段能在工作存儲區能被即時映,下一節我們來講最不常用的關鍵字

?

對于涉及對象初始化的DCL,從JAVA1.5以后雖然可以用volatile來修補,但已經沒有任何意義。因為

?

比它更好的模式lazy initialization hoder可以達到相同的作用而更容易理解:

?

?

?

public MyObject{

?

?

?

??? private static class instanceHoder{//內部私有的類,我特別用了小寫開頭。

?

??????? static MyObject instance = new MyObject();

?

??? }
??? private static MyObect obj;
??? private Date d = new Data();
??? public Data getD(){return this.d;}
??? public static MyObect? getInstance(){

?

???????????return instanceHoder.instance;
??? }
}

?

?

?

private static class instanceHoder是類的定義并不會引起初始化。只有在首次調用getInstance時才會加載

?

instanceHoder類然后初始化instance實例。而靜態初始化是由JVM來保證線程安全的,所以整個過程都不需要同

?

步參與,極大地提高了性能。

轉載于:https://my.oschina.net/vshcxl/blog/876852

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/456790.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/456790.shtml
英文地址,請注明出處:http://en.pswp.cn/news/456790.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

爬蟲的復習手冊

爬蟲的概念 模擬瀏覽器發送請求,獲取響應 爬蟲的流程 url---》發送請求,獲取響應---》提取數據---》保存 發送請求,獲取響應---》提取url(下一頁,詳情頁)重新請求 爬蟲要根據當前url地址對應的響應為準 …

Hive安裝報錯:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient的解決辦法

最近練習Hive,安裝時爆出如下錯誤:Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient的錯誤 報錯的日志如下: Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeE…

要讀

http://www.cnblogs.com/yangml/p/3828878.html轉載于:https://www.cnblogs.com/qinqiu/p/6134683.html

Spark分布式集群的搭建和運行

集群共三臺CentOS虛擬機,一個Matser,主機名為master;三個Worker,主機名分別為master、slave03、slave04。前提是Hadoop和Zookeeper已經安裝并且開始運行。 1. 在master上下載Scala-2.11.0.tgz,復制到/opt/下面&#xf…

Hive2.1.1的安裝教程(元數據放在本地Mysql)

目錄1.上傳tar包2.解壓3. 設置環境變量4.設置Hive的配置文件5.啟動Hive6.安裝MySQL7.下載MySQL的驅動包8.修改Hive的配置文件9.啟動Hive10.查看MySQL數據庫 目錄 1.上傳tar包 jar包地址:http://hive.apache.org/downloads.html 2.解壓 tar -zxvf apache-hive-2…

App性能優化之內存優化

2019獨角獸企業重金招聘Python工程師標準>>> 為什么要進行內存優化呢?其實我們可以反過來想。如果不進行內存優化會產生什么樣的問題? App的運行是有內存限制的,超過限制會產生OOM,導致App崩潰。如果內存不進行優化&am…

python+Tesseract-OCR實現圖片識別(只適合新手)

1.首先準備環境: python版本:2.7/3.6 操作系統:windows系統 2.準備工具: tesseract-ocr 安裝后設置好環境變量 鏈接: https://pan.baidu.com/s/1j8lBbQBrrbPaHAn5ujWFSw 提取碼: 2med Pycharm 3.安裝相關python包&#xf…

Linux 網絡編程詳解四(流協議與粘包)

TCP/IP協議是一種流協議,流協議是字節流,只有開始和結束,包與包之間沒有邊界,所以容易產生粘包,但是不會丟包。 UDP/IP協議是數據報,有邊界,不存在粘包,但是可能丟包。 產生粘包問題…

解決selenium.common.exceptions.WebDriverException: Message: unknown error: call function result missin

(Session info: chrome73.0.3683.103)(Driver info: chromedriver2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platformWindows NT 10.0.17134 x86_64)報錯如上,由于版本不兼容 下面是谷歌瀏覽器與chromedriver的版本對應關系,供參考&#…

執行Hive語句報錯:FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user '

安裝個Hive真不省心,各種問題。最近安裝好Hive后執行Hive語句時碰到這樣的錯誤: hive> show databases; FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user rootlocalhost (using password: YES) NestedThr…

GPU

import tensorflow as tf a tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[2,3],namea) b tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[3,2],nameb) c tf.matmul(a,b)sess tf.Session(configtf.ConfigProto(log_device_placementTrue)) print sess.run(c)

阿里云部署django項目流程【centos7+python3+mysql】

購買阿里云服務器 到[阿里云官網],選擇輕量應用服務器, 步驟如圖所示: 地域隨便選擇哪一個,鏡像的話,對比了CentOS,Debian,Ubuntu,我最終選擇了CentOS,因為流行嘛&…

XidianOJ 1123 K=1 Problem of Orz Pandas

題目描述 One panda named orz is playing a interesting game, he gets a big integer Num and an integer K. In this game, he can exchange two single numbers in Num. For example, he can get 1243 from 3241 by exchange 1 and 3.But orz can exchange at most K times…

對于頻繁的寫數據處理方式

添加一個新的表情的時候 調用 recentEmotions方法 將所有表情寫入數組 每次都是 添加一個新的表情進來 要將沙盒中的所有表情首先加載進數組,然后將表情添加到數組里面 然后在將數組寫入沙盒 處理方式 沒有必要每次都要到沙盒里面讀取數組文件 類方法 不能訪問 成員…

在Mysql中顯示所有用戶的操作教程(Linux環境下)

1.登錄數據庫 首先,你需要使用如下命令登錄到數據庫,注意,必須是root用戶哦~ mysql -u root -p 2.查詢用戶表 在Mysql中其實有一個內置且名為mysql的數據庫,這個數據庫中存儲的是Mysql的一些數據,比如用戶、權限信…

Scrapy 框架【學習筆記01】

Scrapy 框架 Scrapy是用純Python實現一個為了爬取網站數據、提取結構性數據而編寫的應用框架,用途非常廣泛。 框架的力量,用戶只需要定制開發幾個模塊就可以輕松的實現一個爬蟲,用來抓取網頁內容以及各種圖片,非常之方便。 Scra…

通過profile 用maven命令打不同配置的變量包

profiles定義如下<profiles><profile><id>local</id><properties><deploy.type>local</deploy.type></properties></profile><profile><id>dev</id><properties><deploy.type>dev</de…

執行Hive的查詢語句報錯:java.lang.IllegalArgumentException: Does not contain a valid host:port authority: loca

好不容易把Hive裝完了&#xff0c;結果一執行Hive的查詢語句運行MapReduce程序立馬報錯。。。 log詳細信息如下&#xff1a; Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 1; number of reducers: 0 2017-10-21 21:54:15,503…

scrapy startproject【學習筆記02】

入門案例 學習目標 創建一個Scrapy項目定義提取的結構化數據(Item)編寫爬取網站的 Spider 并提取出結構化數據(Item)編寫 Item Pipelines 來存儲提取到的Item(即結構化數據) 一. 新建項目(scrapy startproject) 在開始爬取之前&#xff0c;必須創建一個新的Scrapy項目。進入…

開始把其他的博客搬家到這里了

今天&#xff0c;用一晚上的時間進行一下文章的遷移吧。這樣以后查詢就可以在自己的博客中查找了&#xff0c;也算是給自己一個寫作的規律。 從很多個大牛的博客中都閱讀到&#xff0c;寫博客對于一個coder的重要性。希望這次可以堅持。轉載于:https://www.cnblogs.com/cyforev…