Java併發編程閱讀筆記-Java監視器模式示例

一、前言java

  書中在解釋Java監視器模式的時候使用了一個車輛追蹤器例子,根據不一樣的使用場景給出了不一樣的實現和優化。
安全

二、監視器模式示例數據結構

  實現一個調度車輛的「車輛追蹤器」,每臺車使用一個String對象標識,而且擁有一個相應的位置座標(x,y)。因爲運行在多線程的場景下,對外暴露的接口須要保證線程安全。多線程

  須要提供的接口包括:性能

  1. 獲取全部車輛標識和位置
  2. 讀取某個車輛位置
  3. 更新某個車輛位置

下面給出第一種實現:優化

@ThreadSafe
public class MonitorVehicleTracker {
    @GuardedBy("this")
    private final Map<String, MutablePoint> locations;

    public MonitorVehicleTracker(Map<String, MutablePoint> points) {
        locations = deepCopy(points);
    }

    public synchronized Map<String, MutablePoint> getLocations() {
        return deepCopy(locations);
    }

    public synchronized MutablePoint getLocation(String key) {
        MutablePoint point = locations.get(key);
        return point == null ? null : new MutablePoint(point);
    }

    public synchronized void setLocation(String id, int x, int y) {
        if (id == null) {
            return;
        }
        MutablePoint point = locations.get(id);
        if (point == null) {
            throw new IllegalArgumentException("No such ID: " + id);
        }
        point.setPoint(x, y);

    }

    private Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> points) {
        if (points == null) {
            return Maps.newHashMap();
        }
        Map<String, MutablePoint> result = Maps.newHashMapWithExpectedSize(points.size());
        for (String key : points.keySet()) {
            result.put(key, new MutablePoint(points.get(key)));
        }
        return Collections.unmodifiableMap(result);
    }
}

@NotThreadSafe
public class MutablePoint {
    private int x, y;

    public MutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public MutablePoint() {
    }

    public MutablePoint(MutablePoint point) {
        if (point == null) {
            throw new IllegalArgumentException("param is null");
        }
        int[] pointArray = point.getPointArray();
        x = pointArray[0];
        y = pointArray[1];
    }

    public int[] getPointArray() {
        int[] ret = new int[2];
        ret[0] = x;
        ret[1] = y;
        return ret;
    }

    public void setPoint(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

  首先須要定義一個表示位置的類MutablePoint,該類是可變的,非線程安全的。車輛追蹤器的邏輯實如今類MonitorVehicleTracker,提供了所需的三種接口邏輯。MonitorVehicleTracker是一個線程安全類,經過java內置鎖(synchronized)和深度拷貝實現,返回的位置信息拷貝了當前的數據,包括車輛表示和對應的位置信息。這種實現方式獲得的位置信息是當前的快照,這樣的數據結果是否合適取決於你的需求。this

  上面這個實現使用了深度拷貝的方式,這種方式在車輛數量很是大的時候存在性能問題。那麼是否能夠直接返回原有的數據呢,答案是不能夠,若是直接返回,這樣意味着直接發佈了不支持線程安全的HashMap結構,該數據會在多個線程將共享。spa

  那麼咱們是否有其餘方式解決這個問題呢。一種方案是將線程安全的能力委託給類中內部組件,而java提供了線程安全的HashMap-concurrentHashMap(HashTable、Collections.synchronizedMap()性能不及ConcurrentHashMap)線程

下面給出第二種實現:code

@ThreadSafe
public class MonitorVehicleTracker {
    private final ConcurrentHashMap<String, ImmutablePoint> locations;
    private final Map<String, ImmutablePoint>               unmodifiedLocations;

    public MonitorVehicleTracker(Map<String, ImmutablePoint> pointMap) {
        locations = new ConcurrentHashMap<>(pointMap);
        unmodifiedLocations = Collections.unmodifiableMap(locations);
    }

    public Map<String, ImmutablePoint> getLocations() {
        return unmodifiedLocations;
    }

    public void setLocation(String id, int x, int y) {
        if (StringUtils.isBlank(id)) {
            return;
        }
        if (locations.replace(id, new ImmutablePoint(x, y)) == null) {
            throw new IllegalArgumentException("No such ID: " + id);
        }
    }

    public ImmutablePoint getLocation(String id) {
        if (StringUtils.isBlank(id)) {
            throw new IllegalArgumentException("param is null");
        }
        return locations.get(id);
    }
}

@Immutable
@ThreadSafe
public class ImmutablePoint {
    private final int x, y;

    public ImmutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public ImmutablePoint(MutablePoint point) {
        if (point == null) {
            throw new IllegalArgumentException("param is null");
        }
        int[] pointArray = point.getPointArray();
        x = pointArray[0];
        y = pointArray[1];
    }

    public int[] getPointArray() {
        int[] ret = new int[2];
        ret[0] = x;
        ret[1] = y;
        return ret;
    }
}

  第二個實現中,MonitorVehicleTracker類的線程安全能力委託給內部組件。由於ConcurrentHashMap自己是一個線程安全的HashMap,因此無需進行深度拷貝,直接在線程間共享該數據結構便可。從上面的實現能夠看到,位置信息使用ImmutablePoint而不是MutablePoint,這是由於位置信息也會發布出去,也可能會在線程間共享,而ConcurrentHashMap只能保證自身操做的線程安全。ConcurrentHashMap的key、value都須要是線程安全的,ImmutablePoint使用不變性提供了線程安全,String能夠認爲是常量,一樣支持線程安全。與第一種實現發放不一樣的是,每一個線程拿到的位置信息視圖是一個變化的,並不是快照,若是須要快照,經過淺拷貝便可實現。

  實現一個線程安全的位置信息類還能夠經過內置鎖實現,一樣,整個MonitorVehicleTracker類仍是線程安全的。

  上面這個實現經過委託給支持線程安全的內部組件實現線程安全,那麼是否是隻要內部組件是線程安全的那這個類就是線程安全的呢,顯然不是的,若是內部組件的數據存在邏輯關係,或者存在複合操做時,線程安全須要知足既定的邏輯關係,保證符合操做的原子性,這些都是須要額外的同步操做來完成

  在擴展原有支持線程安全類的時候,無論是經過繼承方式仍是組合方式(客戶端加鎖),都須要保證擴展類中加的鎖和基類的鎖是一個鎖。

相關文章
相關標籤/搜索