【Java并发】JAVA并发编程实战-读书笔记5
来源:互联网 发布:javaee编程技术第二版 编辑:程序博客网 时间:2024/05/16 19:19
设计线程安全类的过程应该包括下面3个基本要素
1,确定对象状态是由哪些变量构成的
2,确定限制状态变量的不变约束
3,制定一个管理并发访问对象状态的策略
将数据封装在对象内部,把对数据的访问限制在对象的方法上,更易确保线程在访问数据时总能获得正确的锁。
public class PersonSer{ private final Set<Person> mySer=new HashSet<Person>(); public synchronized void addPerson(Person p){ mySet.add(p); } public synchronized boolean containsPerson(Person p){ return mySet.contains(p); }}上面的例子是线程安全的,其中未对Person的线程安全性做任何假设,如果他是可变的,那么访问从PersonSet中获得的Person时,还需要额外的同步。为了安全的使用Person对象,最可靠的方法是让Person自身是线程安全的,对Person对象加锁并不十分可靠,因为他还需要所有的用户都遵守协议:访问Person前先获得正确的锁。
public class MonitorVehicleTracker{ private final Map<String,MutablePoint> locations; public MonitorVehicleTracker(Map<String,MutablePoint> locations){ this.locations=deepCopy(locations); } public synchronized Map<String,MutablePoint> getLocations(){ MutablePoint loc=locations.get(id); return loc==null?null:new MutablePoint(loc); } public synchronized void setLoaction(String id,int x,int y){ MutablePoint loc=location.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); }}
public class MutablePoint{ public int x,y; public MutablePoint(){ x=0; y=0; } public MutablePoint(MutablePoint p){ this.x=x; this.y=y; }}我们可以使用Point 代替MutablePoint
public class Point{ public final int x,y; public Point(int x,int y){ this.x=x; this.y=y; }}由于Point类时不可变的,因而是线程安全的,所以我们返回location时不必再复制他们。
使用委托将线程安全交给ConcurrentHashMap
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 setLocation(String id,int x,int y){ if(locations.replace(id,new Point(x,y))==null) throw new IllegalArgumentException(“invalid vehicle name:”+id); }}
public class VisualComponent{ private final List<KeyListener> keyListeners=new CopyOnWriteArrayList<KeyListener>(); private final List<MouseListener> mouseListeners=new CopyOnWriteArrayList<MouseListener>(); pulic 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); }}上面的类使用CopyOnWriteArrayList存储每个监听器清单。这个线程安全的List实现油气适合管理监听器的清单。其中,不但每个List是线程安全的,而且不存在哪个不变约束增加一个状态与另一个状态间耦合。但大多数组合对象不像上面的类那样简单,他们的不变约束与组件的状态变量相互联系。
public class NumberRange{ private final AtomicInteger lower=new AtomicInteger(0); private final AtomicInteger upper=new AtomicInteger(0); public void setLower(int i){ if(i>upper.get()){ throw new IllegalArgumentException(); } lower.set(i); } public void setUpper(int i){ if(i<lower.get()){ throw new IllegalArgumentException(); } upper.set(i); } public boolean isInRange(int i){ return (i>lower.get()&&i<=upper.get()); }}
上面的类不是线程安全的,两个设置方法试图保护不变约束,但是没有适当的加锁,检查再运行时存在风险的。而且也要避免发布lower和upper,以防止用户潜在地破坏不变约束。
如果一个类由多个彼此独立的线程安全的状态变量组成,并且类的操作不包含任何无效状态转换时,可以讲线程安全委托给这些状态变量。
添加一个新的原子操作有两种方式,一是修改原始的类。二是扩展这个类,假设这个类在设计上是可扩展的。但是并非所有的类都给子类暴露了足够多的状态以支持这种方案。因为扩展后,同步策略的实现会被分布到多个独立维护的源代码中,如果底层的类选择了不同的锁来保护她的状态变量,从而会改变他的同步策略,子类就在不知不觉中被破坏,因为他不能再用正确的锁控制对基类状态的并发访问。
public class BetterVector<E> extends Vector<E>{ public synchronized boolean putIfAbsent(E x){ boolean absent=!contains(x); if(absent){ add(x); } return absent; }}
上面的例子是安全的,因为Vector的同步策略已由其规约固定住,所以其子类不会有问题。
对于一个由Collections.synchronizedList封装的ArrayList,两种方法都不正确,因为客户端代码甚至不知道同步封装工厂方法返回的List对象的类型。第三个策略是扩展功能,而不是扩展类本身,并将扩展代码置入一个助手类。
public class ListHelper<E>{ public List<E> list=Collections.synchronizedList(new ArrayList<E>()); pubic synchronized boolean putIfAbsent(E x){ boolean absent=!list.contains(x); if(absent){ list.add(x); } return absent; }}上面的例子不是线程安全的。这里的问题在于同步行为发生在错误的锁上。无论List使用哪个锁保护她的状态,可以确定这个锁并没用到ListHelper上。
为了保证这个方法正确工作,我们必须保证方法使用的锁与List用于客户端加锁与外部加锁时所用的锁时同一个锁。
public class ListHelper<E>{ public List<E> list=Collections.synchronizedList(new ArrayList<E>()); public boolean putIfAbsent(E x){ synchronized(list){ boolean absent=!list.contains(x); if(absent){ list.add(x); } return absent; } }}上面的类时线程安全的。
向已有的类中添加一个原子操作,还可以使用组合。
public class ImprovedList<T> implements List<T>{ private final List<T> list; public ImprovedList(List<T> list){ this.list=list; } public synchronized boolean putIfAbsent(T x){ boolean contains=list.contains(x); if(contains){ list.add(x); } return !contains; } public synchronized void clear(){ list.clear(); }}通过内部锁,ImprovedList引入了一个新的锁层。他并不关心底层的List是否线程安全,即使List不是线程安全的,或者会改变ImprovedList的锁实现,ImprovedList都有自己兼容的锁可以提供线程安全性,虽然会有一部分性能的损失。
0 0
- 《Java并发编程实战》读书笔记
- 《Java并发编程实战》读书笔记
- java并发编程实战-读书笔记
- 《Java并发编程实战》读书笔记
- 《java并发编程实战》读书笔记
- 《Java并发编程实战》读书笔记
- 读书笔记-《Java并发编程实战》
- java并发编程实战读书笔记
- java并发编程实战读书笔记
- Java并发编程实战读书笔记
- 【Java并发】JAVA并发编程实战-读书笔记5
- java并发实战读书笔记
- 【Java并发】JAVA并发编程实战-读书笔记1
- 【Java并发】JAVA并发编程实战-读书笔记2
- 【Java并发】JAVA并发编程实战-读书笔记3
- 【Java并发】JAVA并发编程实战-读书笔记4
- 【Java并发】JAVA并发编程实战-读书笔记6
- 【Java并发】JAVA并发编程实战-读书笔记7
- mysql 利用binlog增量备份,还原实例
- 环境变量
- 不错的Android技术知识
- 在qtcreater中打开qt帮助文档.qch
- shell脚本批量监控Linux server配置文件的更改
- 【Java并发】JAVA并发编程实战-读书笔记5
- Linux内核源码分析方法
- Apple开发账号添加团队成员
- android 主线程与子线程互发消息,以及自己给自己发消息
- poi创建excel文件时,生成单元格下拉选
- 【C#】webbrowser过滤js弹出广告
- Java数组的定义和使用(补充)
- 重写button实现文字图片居中显示
- 关于“策略模式”与“MVP风格”的联系理解心得