Java对象组合,java并发编程的艺术 豆瓣

  Java对象组合,java并发编程的艺术 豆瓣

  00-1010 1.设计线程安全类1.1收集同步需求1.2状态相关操作1.3状态的所有权2。实例闭包2.1 Java监控模式3。线程安全的委托3.1基于委托的车辆跟踪器3.2独立状态变量3.3发布底层状态变量

  

目录
在设计线程安全类的过程中,需要包含以下三个基本要素:

  找出构成物体状态的所有变量。求约束变量的不变性条件。建立对象状态的并发访问管理策略。

  00-1010很多类中都定义了一些不可变的条件来判断状态是否有效。比如计数器的取值范围是有限制的,就是不能为负。运算中会包含一些后验条件来判断状态转移是否有效。例如,如果计数器的当前状态是8,则下一个有效状态只能是9。因为不变性条件和后验条件对状态和状态转换施加了各种约束,所以需要额外的同步和封装。

  00-1010一些基于状态的先验条件也包含在一些对象的方法中。比如:不能从空队列中删除元素。删除元素之前,队列必须处于非空状态。如果一个操作包含基于状态的先验条件,那么这个操作就叫做状态相关操作。

  00-1010一个对象封装了它所拥有的状态,那么它就拥有被封装状态的所有权。状态的所有者将决定采用哪种锁定协议来维护状态的完整性。所有权意味着控制。如果你发布了一个对可变对象的引用,你将不再拥有独占控制权,最多是共享控制权。为了防止多个线程在并发访问同一个对象时相互干扰,这些对象应该是线程安全的、事实不可变的,或者由同一个锁保护。

  00-1010将数据封装在对象内部可以限制数据对对象方法的访问,从而更容易确保线程在访问数据时始终持有正确的锁。封闭的对象不能超出它们的既定范围。对象可以包含在类的实例中,例如,作为类的私有成员;或者封闭在某个范围内,例如,作为局部变量;或者在线程中关闭,例如在线程中将对象从一个方法传递到另一个方法,而不是在多个线程之间共享对象。

  关闭机制更容易构造线程安全的类,因为当一个类的状态为关闭时,在分析该类的线程安全时,不需要检查整个程序。

  00-1010遵循Java monitor模式的对象将封装对象的所有变量状态,并由对象自己的内置锁保护。例如:用于调度车辆的“车辆跟踪器”。每辆车由一个字符串对象标识,并有一个相应的位置坐标(x,y)。创建一个tracker类,封装所有车辆的标识和位置,由多个线程共享(读取操作和更新操作)。

  @ notthreadsafepointclass mutable point { public int x,y;public mutable point(){ x=0;y=0;} public mutable point(mutable point p){ this . x=p . x;this . y=p . y;}}@ThreadSafepublic类monitor vehicle tracker { @ guarded by( this )private final MapString,MutablePoint locationspublic monitor vehicle tracker(MapString,mutable point locations){ this . locations=deep copy(locations);} public synchronized MapString,mutable point get locations(){ return deep copy(locations);} public synchronized mutable point get location(字符串id){ mutable point loc=locations . get(id);return loc==null?null :新的可变点(loc);} public synchronized void set location(String id,int x,int y){ mutable point 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不是线程安全的,但是追踪器类是线程安全的。它所包含的Map对象和可变的Point对象都未曾发布。当需要回去车辆的位置时,通过拷贝构造函数或deepCopy方法复制正确的值,从而生成一个新的Map对象。车辆少是,这并不存在性能问,单在车辆数量非常大的情况下将极大地降低性能。

  

  

3. 线程安全性的委托

大多数对象都是组合对象。当从头开始构建一个类,或者将多个非线程安全的类组合为一个类时,Java监视器模式是非常有用的。但是,如果类中的各个组件都已经是线程安全的,那么实现线程安全就应视情况而定。

  

  

3.1 基于委托的车辆追踪器

保存车辆位置信息的Map类,使用线程安全的ConcurrentMap类代替。

  使用不可变的Point类来代替MutablePoint类:

  

public class Point { public final int x, y; public Point(int x, int y) { this.x = x; this.y = y; }}
在DelegatingVehicleTracker中没有使用显式的同步,所有对状态的访问都由ConcurrentHashMap来管理:

  

@ThreadSafepublic 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 Map<String, Point> getLocations() { return unmodifiableMap; } 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); }}
在使用监视器模式的车辆追踪器中返回的是车辆位置的快照,而在使用委托的车辆追踪器中返回的是一个不可修改却实时的车辆位置信息。

  

  

3.2 独立的状态变量

我们还可以将线程安全性委托给多个状态变量,只要这些变量是彼此独立的,即组合而成的类并不会在其包含的多个状态变量上增加任何不变形条件。

  

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); }}
VisualComponent类是一个图形组件,允许客户程序注册监控鼠标和键盘等事件的监听器。在鼠标事件监听器和键盘事件监听器之间不存在任何关联,二者是彼此独立的,因此VisualComponent可以将其线程安全性委托给这两个线程安全的监听器列表。

  如果一个类是由多个独立且线程安全的状态变量组成,并且在所有的操作中都不包含无效状态转换,那么可以将线程安全性委托给底层的状态变量。

  

  

3.3 发布底层的状态变量

如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全地发布这个变量。我们来修改之前的车辆追踪器的代码,使其发布底层的可变状态。

  首先写一个可变并且线程安全的Point类:

  

public class SafePoint { @GuardedBy("this") private int x, y; private SafePoint(int[] a) { this(a[0], a[1]); } public SafePoint(SafePoint p) { this(p.get()); } public SafePoint(int x, int y) { this.set(x, y); } public synchronized int[] get() { return new int[]{x, y}; } public synchronized void set(int x, int y) { this.x = x; this.y = y; }}
再修改追踪器的代码:

  

public class PublishingVehicleTracker { private final Map<String, SafePoint> locations; private final Map<String, SafePoint> unmodifiableMap; public PublishingVehicleTracker(Map<String, SafePoint> locations) { this.locations = new ConcurrentHashMap<String, SafePoint>(locations); this.unmodifiableMap = Collections.unmodifiableMap(this.locations); } public Map<String, SafePoint> getLocations() { return unmodifiableMap; } public SafePoint getLocation(String id) { return locations.get(id); } public void setLocation(String id, int x, int y) { if (!locations.containsKey(id)) throw new IllegalArgumentException("invalid vehicle name: " + id); locations.get(id).set(x, y); }}
PublishingVehicleTracker中,其线程安全性委托给底层的ConcurrentHashMap,只是Map中的元素是线程安全的可变的。getLocations方法返回底层Map对象的一个不可变副本,调用者不能增加或删除车辆,但可以通过修改返回Map中的SafePoint值来修改车辆的问题。

  到此这篇关于Java并发编程之对象的组合的文章就介绍到这了,更多相关Java对象的组合内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: