-
(1) 从头开始构建一个类, 或将多个非线程安全的类组合为一个类时, Java监视器模式很有效(见"2_实例封闭.md");
多个线程安全的类组合为一个类时, 可以考虑__委托方式__
(2) 示例
1° 基于监视器模式的车辆追踪
MonitorVehicleTracker.java
@ThreadSafe public class MonitorVehicleTracker { @GuardedBy("this") private final Map<String, MutablePoint> locations; public MonitorVehicleTracker(Map<String, MutablePoint> locations) { this.locations = deepCopy(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 synchronized 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<String, MutablePoint>(); for (String id : m.keySet()) { result.put(id, new MutablePoint(m.get(id))); } return Collections.unmodifiableMap(result); } }
MutablePoint.java
@NotThreadSafe 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; } }
这个示例中, 虽然MutablePoint线程不安全, 但是MonitorVehicleTracker是安全的: 它采用了监视器模式, 用this作为锁, 并且内含的Map对象和Mutable对象都未曾发布
2° 基于委托的车辆追踪
DelegatingVehicleTracker.java
@ThreadSafe public class DelegatingVehicleTracker { private final ConcurrentMap<String, Point> locations; private final Map<String, Point> unmodifiableMap; public DelegatingVehicleTracker(Map<String, Point> points) { locations = new ConcurrentHashMap<String, Point>(points); unmodifiableMap = Collections.unmodifiableMap(locations); } public Point getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (locations.replace(id, new Point(x, y)) == null) { throw new IllegalArgumentException("invalid vehicle name: " + id); } } public Map<String, Point> getLocations() { return unmodifiableMap; } // Alternate version of getLocations public Map<String, Point> getLocationsAsStatic() { return Collections.unmodifiableMap(new HashMap<String, Point>(locations)); } }
Point.java
@Immutable public class Point { public final int x, y; public Point(int x, int y) { this.x = x; this.y = y; } }
这种方式将线程安全委托给了ConcurrentMap类型的locations, 由于Point类是不可变的,所以如果想要实时更新点的位置, 可以调用getLocations()函数, 这样更新会立刻显示; 如果不需要发生变化的车辆位置, 可以调用getLocationsAsStatic()函数, 在这里采用了__浅拷贝__的方式(因为Point不可变, 复制Map的结构即可, 不需要复制它的内容)
(3) 线程安全性可以委托给多个状态变量, 只要它们是彼此独立的
示例
public class VisualComponent { private final List<KeyListener> keyListeners = new CopyOnWriteArrayList<KeyListener>(); private final List<MouseListener> mouseListeners = new CopyOnWriteArrayList<MouseListener>(); public void addKeyListener(KeyListener listener) { keyListeners.add(listener); } public void addMouseListener(MouseListener listener) { mouseListeners.add(listener); } public void removeKeyListener(KeyListener listener) { keyListeners.remove(listener); } public void removeMouseListener(MouseListener listener) { mouseListeners.remove(listener); } }
这个示例中, keyListeners和mouseListeners的类型是CopyOnWriteArrayList, 这是一个线程安全的容器类型, 并且由于它们互相独立, 所以VisualComponent的线程安全委托给了keyListeners和mouseListeners
(4) 但是, 如果某个类含有复合操作, 那么委托方式就会失效, 此时必须依靠类自己的加锁机制(这个类似于如果只有一个状态变量时用volatile就行,但是有多个变量仅用volatile就会失效)
chapter04_对象的组合_3_线程安全性的委托
最新推荐文章于 2023-12-17 19:50:18 发布