今天終於又從新拿起了Java Concurrency in Practice,以前被虐的體無完膚,在看這本書以前,有一部分本身寫的代碼我根本沒意識到是線程不安全的,還真的是要惡補這方面的知識。安全
監視器模式其實很簡單,就是用私有對象的鎖或者內置鎖來保證所屬對象的線程安全性。這裏引入一個例子:車輛追蹤性能
public class MonitorVehicleTracker { private final Map<String, MutablePoint> locations; public MonitorVehicleTracker(Map<String, MutablePoint> locations) { this.locations = locations; } public synchronized Map<String, MutablePoint> getLocations() { return deepCopy(locations); } public synchronized MutablePoint getLocation(String id) { MutablePoint loc = locations.get(id); return loc == null ? null : new MutablePoint(loc); } public void setLocation(String id, int x, int y) { MutablePoint loc = locations.get(id); if(loc == null) throw new IllegalArgumentException("No such ID: " + id); loc.x = x; loc.y = y; } private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) { Map<String, MutablePoint> result = new HashMap<>(); for(String id : m.keySet()) result.put(id, new MutablePoint(m.get(id))); return Collections.unmodifiableMap(result); }
這裏MutablePoint是個可變類,是線程不安全的:this
public class MutablePoint { public int x, y; public MutablePoint() { x = 0; y = 0; } public MutablePoint(MutablePoint p) { this.x = p.x; this.y = p.y; } }
細心的讀者確定發現了,MonitorVehicleTracker類中幾乎每個方法都要複製MutablePoint或者locations的值,緣由在於,這倆不是線程安全的,不能直接發佈出去,因此只能發佈一個副本,可是這又出了新的問題:雖然MonitorVehicleTracker類是線程安全的,可是因爲數據都是複製的,那麼假設線程A調用了getLocations()方法獲得了位置,此時車的位置變化,線程B調用setLocation()修改了內部變量locations,這時車輛的位置已經修改了,可是線程A返回的仍是舊的位置。固然,若是爲了保持數據的一致性,那麼這樣作就是優勢;但若是想要獲得車輛的實時位置,就不得不獲得車輛位置的最新快照,上述的方法會形成嚴重的性能問題。那麼如何改進呢?在這裏我給個提示:複製數據的緣由是由於屬性是線程不安全的,不能直接發佈,那麼,若是發佈一個線程安全的屬性,是否就解決了實時性的問題?spa