并发并行编程是当前热点,过去我们知道使用锁synchronization来解决多线程并发访问同一个数据结构时共享问题,甚至我们怀疑数据共享方式本身是不是就错了?所以,云计算的数据喂任务模式开始盛行,但是数据共享方式从我们开始软件第一天就已经习惯,如何在这个共享模式下实现高并发访问呢?也就是不使用锁synchronization,那么就通过不变性Immutable模式来实现。
Immutable Data Structures in Concurrent Java Applications提出了实现集合对象边读边修改的并发实现方式。
首先指出volatile 的不足,因为不能保证操作volatile 字段方法的原子性,这样,还是需要锁synchronization来修饰其操作方法,该文提出使用final来替代volatile,如果需要修改final的字段值,就用这个对象来替换,这个概念符合DDD中值对象定义,值对象是不可变的,一旦变化,整个对象更换,同时也符合并发模型,如下类:
public final class Contact { private final String name; private final String email; private final phone;
public Contact(String name, String email, String phone) { this.name = name; this.email = email; this.phone = phone; }
public String getName() {return name;} public String getEmail() {return email;} public String getPhone() {return phone;} }
|
如果我们有一个Contact对象的集合:联系人名单集合,然后给这个名单中每个联系人发送Email:
public void sendMessages(Map contactMap) {
sendEmail(contactMap.values());
}
contactMap是Contact集合,contactMap.values是遍历contactMap中元素Contact对象。如果在遍历发生Email同时,有新的Contact对象加入到contactMap集合中,这时会抛出并发错误。当然,可以使用ConcurrentMap来实现Map。
但是该文提出一个不可变Map也许能获得更好地并发性能。
public class ImmutableMap implements Map { private final Map map;
public ImmutableMap() { this(Collections.EMPTY_MAP); }
public ImmutableMap immutablePut(K key, V value) { Map newMap = new HashMap(map); newMap.put(key, value); return new ImmutableMap(newMap);//创新新的不可变Map }
public ImmutableMap immutableRemove(K key) { Map newMap = new HashMap(map); newMap.remove(key); return new ImmutableMap(newMap); }
private ImmutableMap(Map delegate) { this.map = Collections.unmodifiableMap(delegate); }
// All the map methods are simply delegated to the map field. // To conserve space they are not shown here. }
|
该Map的特点就是遵循值对象模型的特点,集合Map作为一个值对象模型,一旦其元素发生变化,如新增或删除元素,返回一个新的集合Map对象。获得使用该不可变Map的代码如下:
public class ContactService { private final ReentrantLock lock = new ReentrantLock(); //注意 volatile private volatile ImmutableMap contacts = new ImmutableMap();
public void addContact(Contact contact) { lock.lock();//使用锁来实现变动 try { contacts = contacts.immutablePut(contact.getName(), contact); } finally { lock.unlock(); } }
public ImmutableMap getContacts() { return contacts; } }
|
这样,通过避免使用锁synchronization,而是通过业务设计出值对象,然后使用不可变模式来获得更好地性能,从这里也可以看出面向对象设计并不会影响性能,反而能提升性能。