摘要:線程安全需求分析三個例子都是關于車輛追蹤的。他們使用了不同的方式來保證車輛追蹤類的線程安全性。值得注意的值文檔也是維護線程安全的重要組成部分。
每個例子后面有代碼,大家可以先把代碼粘出來或者開兩個頁面,先過一下例子的代碼,然后一邊看分析一遍看代碼,上下拖動看的話效果不好。
歡迎拍磚和補充。
線程安全需求分析三個例子都是關于車輛追蹤的。他們使用了不同的方式來保證車輛追蹤類的線程安全性。
我們知道,如果要寫一個線程安全類,那么首先得明確這個類關于線程安全的需求。
那么這個類的線程安全需求就是:
訪問線程要么能夠看到寫線程對location的x,y坐標完整的寫入,要么看不到。不允許出現訪問線程只看到寫線程寫了其中一個坐標。
比如線程A在訪問location時,只看到了B線程對location的x或者y坐標的寫入,那就破壞了這個類的線程安全性。
例子1(MonitorVehicleTracker ) 對 線程不安全+可變 對象進行實例封閉和加鎖作者使用了實例封閉+加鎖機制保證了MonitorVehicleTracker類的安全性。
實例封閉的意思就是將狀態的訪問路徑限制在對象內部,
實例限制后,只要對這些狀態的訪問自始至終使用同一個鎖,就能保證其線程安全性。
MonitorVehicleTracker的唯一狀態:locations,是一個HashMap對象,大家都知道它是可變的,也是線程不安全的。
構造函數和getLocations都做了一次deepCopy。這兩個deepCopy都是必須的。deepCopy保證了將locations對象封裝在了MonitorVehicleTracker實例中,向外發布的只是一個拷貝的副本。想要訪問locations這個狀態,只能通過MonitorVehicleTracker對象,而所有的訪問路徑,都加上了鎖。
deepCopy方法返回結果使用了Collections.unmodifiableMap(map):這里不使用UnmodifiableMap,而是只是用deepCopy的HashMap也是可以的,但是文檔一定寫清楚,返回的是deepCopy的Map。不然站在調用者的角度,如果對其進行寫操作,就不能獲得期望的結果。
值得注意的值 文檔也是維護線程安全的重要組成部分。
就好比SynchronizedCollection的子類,這些同步容器在調用iterator方法時并沒有加鎖。
導致如果用戶需要讀寫一致,那么在迭代的時候必須加鎖,而且這個鎖必須是創建的SynchronizedXXX對象本身。
如果對讀寫一致沒那么敏感,那迭代的時候只需要處理一下ConcurrentModificationException即可。
這些都是在SynchronizedXXX文檔上寫的很清楚的。
由于MutablePoint是可變的,如果deepCopy在迭代時不對每一個location進行復制map.put(k, new MutablePoint(v.x,v.y));而是使用map.put(k, v);那getLocations發布出去的所有location就存在線程安全風險,因為在外部其他線程得到location之后有可能對其進行更新。getLocation是同樣的道理,發布出去的MutablePoint一定是副本。
locations字段的final是不是必須的?我認為不是,因為這里并不是希望把MonitorVehicleTracker變成不可變對象。
如果沒有final,那就必須注意,要使用安全的方式來發布MonitorVehicleTracker對象。
安全發布參見原書3.5節或者這個例子https://segmentfault.com/q/10...)
代碼public class MonitorVehicleTracker { private final Map例子2(DelegatingVehicleTracker)locations; public MonitorVehicleTracker(Map locations) { this.locations = deepCopy(locations); } public synchronized Map getLocations() { return deepCopy(locations); } public synchronized MutablePoint getLocation(String id) { MutablePoint location = locations.get(id); return location == null ? null : new MutablePoint(location.x, location.y); } public synchronized void setLocation(String id, int x, int y) { MutablePoint point = locations.get(id); if (point != null) { point.x = x; point.y = y; } else { throw new IllegalArgumentException("No such Id:" + id); } } private Map deepCopy(Map locations) { Map map = new HashMap<>(); locations.forEach((k, v) -> { map.put(k, new MutablePoint(v.x, v.y)); }); return Collections.unmodifiableMap(map); } } /** * 可變,線程不安全 */ class MutablePoint { public int x, y; public MutablePoint(int x, int y) { this.x = x; this.y = y; } }
此類對訪問locations的所有方法都沒有加鎖,而是通過使用線程安全的ConcurrentHashMap來保證DelegatingVehicleTracker的線程安全性。相當于是把線程安全行委托給了ConcurrentHashMap。
getLocations使用了UnmodifiableMap作為視圖返回。如果不使用UnmodifiableMap而是直接返回locations行不行?
我認為是可以的,畢竟locations是ConcurrentHashMap類型,它是線程安全的,并且作為DelegatingVehicleTracker的一個狀態,并沒有什么約束條件,或者不允許有的狀態遷移操作。
這里使用UnmodifiableMap只是增強了封裝性,意味著,你想修改車輛位置,那必須通過DelegatingVehicleTracker對象上的方法來操作。
作者還提到另一種方法:下邊代碼中的getCopyedLocations方法。
這個方法和getLocations方法的區別是前者在不能夠實時地反應車輛位置的變化,而后者可以。
因為Collections.unmodifiableMap(new HashMap<>(locations));在new HashMap時做了putAll.它將locations的所有元素淺復制了一份。所以當locations有寫入操作時,HashMap并不能得知。
而Collections.unmodifiableMap(locations)是將locations的引用保存在了UnmodifiableMap中,
所以當locations有寫入操作時,UnmodifiableMap可以立即看到。
public class DelegatingVehicleTracker { private final ConcurrentHashMap例子3(PublishVehicleTracker)locations; private final Map locationsView; public DelegatingVehicleTracker(Map points) { this.locations = new ConcurrentHashMap<>(points); this.locationsView = Collections.unmodifiableMap(this.locations); } public Map getLocations() { return locationsView; } public Map getCopyedLocations() { return Collections.unmodifiableMap(new HashMap<>(locations)); } public ImmutablePoint getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (locations.replace(id, new ImmutablePoint(x, y)) == null) { throw new IllegalArgumentException("No such id:" + id); } } } class ImmutablePoint { private final int x, y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } }
此類和DelegatingVehicleTracker的區別:
使用了線程安全的SafePoint。
setLocation方法不再replace一個新構造的ImmutablePoint。
因為SafePoint和ConcurrentHashMap都是線程安全的,
所以這幾個方法都不需要額外的同步,或者復制,直接調用他們的修改狀態的方法是沒問題的。
代碼public class PublishVehicleTracker { private final ConcurrentHashMaplocations; private final Map locationsView; public PublishVehicleTracker(Map points) { this.locations = new ConcurrentHashMap<>(points); this.locationsView = Collections.unmodifiableMap(this.locations); } public Map getLocations() { return locationsView; } public SafePoint getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (!locations.contains(id)) { throw new IllegalArgumentException("No such id:" + id); } locations.get(id).setXY(x, y); } } class SafePoint { private int x, y; public SafePoint(int x, int y) { this.x = x; this.y = y; } public synchronized int[] getXY() { return new int[]{x, y}; } public synchronized void setXY(int x, int y) { this.x = x; this.y = y; } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67854.html
摘要:學習編程的本最佳書籍這些書涵蓋了各個領域,包括核心基礎知識,集合框架,多線程和并發,內部和性能調優,設計模式等。擅長解釋錯誤及錯誤的原因以及如何解決簡而言之,這是學習中并發和多線程的最佳書籍之一。 showImg(https://segmentfault.com/img/remote/1460000018913016); 來源 | 愿碼(ChainDesk.CN)內容編輯 愿碼Slo...
摘要:在添加新項目時使用堆棧,將堆棧的頂部放在新項目之后。當從堆棧中彈出一個項目時,在返回項目之前,您必須檢查另一個線程自操作開始以來沒有添加其他項目。比較和交換將堆棧的頭部設置為堆棧中舊的第二個元素,混合完整的數據結構。 Abstract Treiber Stack Algorithm是一個可擴展的無鎖棧,利用細粒度的并發原語CAS來實現的,Treiber Stack在 R. Kent T...
摘要:對于域,編譯器和處理器要遵守兩個重排序規則在構造函數內對一個域的寫入,與隨后把這個被構造對象的引用賦值給一個引用變量,這兩個操作之間不能重排序。這個屏障禁止處理器把域的寫重排序到構造函數之外。下一篇深入理解內存模型七總結 與前面介紹的鎖和volatile相比較,對final域的讀和寫更像是普通的變量訪問。對于final域,編譯器和處理器要遵守兩個重排序規則: 在構造函數內對一個fi...
摘要:能夠異步的執行任務,并且通常管理一個線程池。這樣我們就不用手動的去創建線程了,線程池中的所有線程都將被重用。在之后不能再提交任務到線程池。它不使用固定大小的線程池,默認情況下是主機的可用內核數。 原文地址: Java 8 Concurrency Tutorial: Threads and Executors Java 5 初次引入了Concurrency API,并在隨后的發布版本中...
閱讀 2755·2021-09-24 09:47
閱讀 4378·2021-08-27 13:10
閱讀 3028·2019-08-30 15:44
閱讀 1293·2019-08-29 12:56
閱讀 2600·2019-08-28 18:07
閱讀 2622·2019-08-26 14:05
閱讀 2579·2019-08-26 13:41
閱讀 1272·2019-08-26 13:33