java并发编程笔记day04
来源:互联网 发布:销售数据周报模板xls 编辑:程序博客网 时间:2024/06/06 18:42
4.3 委托线程安全
4.3.1 范例:试用委托的机动车追踪器
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 Map<String,Point> getLocations(){ return unmodifiableMap; } public Point getLocation(String id ){ return locations.get(id); } public void setLocaltion(String id, int x, int y ){ if (locations.replace(id, new Point(x, y)) == null ) { throw new IllegalArgumentException("invilad vehicle name :"+id); } }}
public class Point { public final int x,y; public Point(int x,int y){ this.x = x; this.y = y; }}
我们在这里对VehicleTracker 类进行了略微的改变:基于监听器的代码返回localtion的快照,基于委托的代码则返回一个不可变的,但是现场的localtion视图。
如果需要一个不可变的瞬时视图,getLocaltion可以返回一个localtions的Map的灰拷贝,只复制对象的引用,因此复制的对象与原始的对象是同一个对象,相反深拷贝复制的是对象的所有成员变量,因此复制的对象与原始的对象不是一个对象。因为Map的内容是不变的,因此需要复制的只有map的结构而不包括它的内容,如下代码所示:
public Map<String, Point> getLocaltions(){ return Collections.unmodifiableMap( new HashMap<String,Point>(locations)); }
4.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 类中,我们的每个list都是线程安全的,而且不存在哪个不变约束会增加一个状态与另一个状态间的耦合,所以VisualComponent 可以将它的线程安全委托到keyListeners 和mouseListeners 上。
4.3.3 当委托无法胜任时
如果像下面代码使用两个AtomicInteger管理NumberRange的状态,而且受到一个额外的约束条件–第一个数小于或等于第二个数。
NumberRange 是线程不安全的,因为它没有保护好用于约束lower和uper的不变约束。setUpper和setLower试图保护不变约束,但是明显办不到。setUpper和setLower都是先检查后运行的操作,但是它们没有适当的加锁以保证其原子性。
假设初始为(0,10) 当一个线程调用setlower(6) 另一个线程调用setupper(4)此时并发执行,先检查的条件都是满足的,那么执行之后的数据为(6,4)一个无效的状态。这个例子很明显的告诉我们虽然AtomicInteger 是线程安全的,但是组合起来就不一定,因为lower和upper不是彼此独立的。
public class NumberRange { private final AtomicInteger lower = new AtomicInteger(0); private final AtomicInteger upper = new AtomicInteger(0); public void setLower(int i) { if (i > lower.get()) { throw new IllegalArgumentException("cannot set lower to " + i + ">lower"); } lower.set(i); } public void setUpper(int i) { if (i < upper.get()) { throw new IllegalArgumentException("cannot set upper to " + i + ">upperr"); } upper.set(i); } public boolean isInRange(int i) { return (i >= lower.get() && i <= upper.get()); }}
4.3.4 发布底层的状态变量
如果一个类是线程安全的,没有任何不变约束限制它的值,并且没有任何状态转换限制它的操作,那么它可以被安全的发布。
举个例子 ,大栗子 !QUQ 我们上面的VisualComponent 发布mouseListeners 或者
keyListeners 是安全的,因为VisualComponent 没有对监听器清单的合法状态进行限制,所以把这些域声明为public,或者发布它们,都不会危及到线程的安全。
4.3.5 示例:发布了状态的机动车追踪器
又是一个大栗子。。。啦啦啦。。。
我们构造一个发布了底层状态的版本的机动车追踪器,我们要改变一下接口适应,这次试用可变但线程安全的Point,我们这里的Point提供了一个get函数获得一个包含x,y的二位数组。因为我们如果单独为x,y提供get方法的话那么我们获得的x,y的值可以是在两个坐标上的x,y会导致此时取到的x,y位置上并没有我们的机动车。我们使用SafePoint来构建一个机动车追踪器,它发布了底层的可变状态,缺不破话线程的安全性。
@ThreadSafepublic 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.x= x; this.y=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 unmodifiableMap.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的内容是线程安全的可变Point,而非不变的。getlocation返回底层map的不可变拷贝,调用者在其上无法添加或移除车辆,却可以修改返回的map中的safepoint的值来达到修改一辆机动车的位置。map的这一现场特性是否有价值,还是一个缺陷,任然取决于需求。只有PublishingVehicleTracker 对机动车追踪器的合法值没有施加任何额外的约束时,它才是现场安全的。如果我们对location的改变进行判断或者执行一些其他操作,那么PublishingVehicleTracker 的做法可能就不正确了
- java并发编程笔记day04
- [Java视频笔记]day04
- Java基础Day04笔记
- 【java笔记】day04
- java并发编程笔记
- Java并发编程笔记
- Java并发编程笔记
- Java并发编程笔记
- JAVA并发编程笔记
- Android学习笔记 day04 _ 网络编程
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- java并发编程实践笔记
- 正则关系式
- http的工作原理
- Android 开发者必知必会的权限管理知识
- c的关于数组初始化 和 memset用法
- HDU-2026 首字母变大写
- java并发编程笔记day04
- FP-growth算法(一)——通过构建FP树发现频繁项集
- HTTP 消息结构
- 01背包问题 动态规划 c语言实现
- 手把手教你入门--Pycharm
- caffe添加C++层和python层定义
- eclipse配置
- 图解误删库文件之修复方法
- nyoj 488 素数环