国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java并發編程

2019-11-14 21:33:41
字體:
來源:轉載
供稿:網友
java并發編程 - 線程封閉

保證并發安全性的方式有三:

不共享、不可變、同步

前兩種方式相對第三種要簡單一些。

這一篇不說語言特性和API提供的相關同步機制,主要記錄一下關于共享的一些思考。

共享(shared),可以簡單地認為多個線程可以同時訪問某個對象。

如果僅僅在單線程內進行訪問則不存在同步的問題。

保證數據的單線程訪問稱為線程封閉(thread confinement)。

線程封閉有三種方式:

·Ad-hoc線程封閉

·棧封閉

·ThreadLocal

Ad-hoc線程封閉

通過程序實現來進行線程封閉,也就是說我們無法利用語言特性將對象封閉到特定的線程上,這一點導致這種方式顯得不那么可靠。

舉個例子,假設我們保證只有一個線程可以對某個共享的對象進行寫入操作,那么這個對象的"讀取-修改-寫入"(比如自增操作)在任何情況下都不會出現竟態條件。

如果我們為這個對象加上volatile修飾則可以保證該對象的可見性,任何線程都可以讀取該對象,但只有一個線程可以對其進行寫入。

這樣,僅僅通過線程封閉+volatile修飾就適當地保證了其安全性,相比直接使用synchoronized修飾,雖然更適合,但實現起來稍微復雜。

而對于線程封閉方式的選擇,這種方式是最不被推薦的。

棧封閉

這個方式理解起來比較簡單,封閉在執行線程是局部變量本身固有的特性,封閉在執行線程的棧里,其他線程無法訪問是理所當然的。

對于基本類型的局部變量,我們不用考慮任何事情,因為Java語言特性本身就保證了任何方法都無法獲得基本類型的引用。

而對于引用類型的局部變量,我們需要稍微注意一些問題來保證其棧封閉。

參考下面的裝載方舟的代碼,現在我們要保護animals,則需要保證該方法的參數、調用的外來方法、返回值都不會引用到animals:

123456789101112131415161718publicintloadTheArk(Collection<Animal>candidates){SortedSet<Animal>animals;intnumPairs=0;Animalcandidate=null;animals=newTreeSet<Animal>(newSpeciesGenderComparator());animals.addAll(candidates);for(Animala:animals){if(candidate==null||!candidate.isPotentialMate(a))candidate=a;else{ark.load(newAnimalPair(candidate,a));++numPairs;candidate=null;}}returnnumPairs;}

先說說loadTheArk的參數candidates,我們將它的元素進行篩選后裝載到了方舟中,方法結束后無法通過該參數影響方舟中的動物夫婦。

其次是外來方法,我們使用了"種類性別比較器"對animals進行排序,但它是一個concrete,不會有不確定的行為對animals的狀態產生影響。

最后是返回值,顯然我們是想報告裝載了多少對動物夫婦,返回類型是個基本類型,無法引用animals。

好了,這就是個成功的棧封閉。

ThreadLocal

給人一種親切感,這幾乎是很常見的方式,而且也是最規范的方式。

我們通常用ThreadLocal保證可變的單例變量和全局變量不被多線程共享。

先讓我們想想單線程場景中使用Connection對象連接數據庫,鑒于Connection對象的初始化開銷,整個應用中會維護一個全局的Connection對象。

如果我們想將這個應用改為多線程的,鑒于Connection對象本身不是線程安全的,我們需要對其進行線程封閉,此時我們可以使用ThreadLocal:

123456789101112131415161718publicclassConnectionDispenser{staticStringDB_URL="jdbc:MySQL://localhost/mydatabase";PRivateThreadLocal<Connection>connectionHolder=newThreadLocal<Connection>(){publicConnectioninitialValue(){try{returnDriverManager.getConnection(DB_URL);}catch(SQLExceptione){thrownewRuntimeException("UnabletoacquireConnection,e");}};};publicConnectiongetConnection(){returnconnectionHolder.get();}}

不僅是Connection這種場景,如果我們的很多操作頻繁地用到某個對象,而我們又需要考慮它的線程封閉又需要考慮它的初始化開銷,ThreadLocal幾乎是最好的選擇。

雖然這看起來有點像一個全局的Map<Thread,T>,事實上也可以這樣理解,但其實現并不是這樣你懂的。

當然,這種方式很方便,但這并不代表ThreadLocal可以濫用, 比如僅僅是考慮到應用的并發安全性就把全局變量一律變成ThreadLocal。

而這種做法會導致全局變量難以抽象,并降低其可重用性,而且也增加了耦合。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宁晋县| 盐津县| 嘉善县| 收藏| 马山县| 思茅市| 泾源县| 公主岭市| 三穗县| 海晏县| 宝坻区| 大化| 崇州市| 高青县| 榆树市| 板桥市| 迭部县| 翁牛特旗| 镇安县| 辽中县| 儋州市| 隆子县| 游戏| 龙泉市| 察隅县| 庆阳市| 黄大仙区| 龙江县| 贵港市| 民乐县| 安乡县| 囊谦县| 灌云县| 临江市| 姚安县| 呼图壁县| 乌鲁木齐县| 西和县| 曲阳县| 和硕县| 政和县|