Google Collections(Guava)中强大的Concurrent MapMaker

仔细研究了刚发布1.0版本的Google Collections,被其中的MapMaker震惊,这不就是我梦寐以求的Concurrent Map神器吗?如果Google Collection在5年前就发布该有多好?!废话少讲,邀请大家一起来观赏一下什么是MapMaker。

Hashtable太老土啦,线程安全我都用ConcurrentHashMap。什么?现在流行MapMaker? 

JDK 1.5引入的ConcurrentHashMap由于其精巧的设计,更高的并发性能,捕获了大家的心,在并发场景中出场率极高,但随着深入的使用,很快的就发现了其中的不足。例如在以Map作为Cache的典型场景中,我们都需要有元素过期的处理,WeakHashMap是这方面的高手,但其在并发方面有点菜(非线程安全),当我们想让这两位大将同时上场的时候,就只能抓耳搔腮了。

Google Collections中的MapMaker融合了Weak Reference,线程安全,高并发性能,异步超时清理,自定义构建元素等强大功能于一身。(注)

常阅读优秀源代码的童鞋都知道,一般叫Maker的对象都是Builder模式,而这个MapMaker就是来"Build"Map的,下面的代码展示了如何构建一个高并发性能,线程安全的WeakHashMap.

Java代码 
public void testWeakKeys() throws Exception { 
    ConcurrentMap<Key, Value> map = new MapMaker() 
        .weakKeys() // 指定Map保存的Key为WeakReference机制 
        .makeMap();  
 
    Key key = new Key(); 
    map.put(key, new Value()); // 加入元素 
    key = null; // key变成了WeakReference 
 
    System.gc();// 触发垃圾回收 
    TimeUnit.SECONDS.sleep(1L); 
 
    assertTrue(map.isEmpty()); // map空了,因为WeakReference被回收 


是不是够简单?他不仅支持WeakKeys,还支持WeakValues。
Java代码 
public void testWeakValues() throws Exception { 
    ConcurrentMap<Key, Value> map = new MapMaker() 
        .weakValues() // 指定Map保存的Value为WeakReference机制 
        .makeMap();  
 
    Key key = new Key(); 
    Value value = new Value(); 
    map.put(key, value); // 加入元素 
    key = null; // Key成了WeakReference 
     
    System.gc();// 触发垃圾回收 
    TimeUnit.SECONDS.sleep(1L); 
     
    assertFalse(map.isEmpty()); // map里的东西还在,因为Value还是StrongReference 
     
    value = null; // 这次value也变成了WeakReference 
 
    System.gc(); // 触发垃圾回收 
    TimeUnit.SECONDS.sleep(1L); 
 
    assertTrue(map.isEmpty()); // map真空了,因为Value是WeakReference被回收 



还可以选用SoftKeys,和SoftValues,随意组合,比只能WeakKey的WeakHashMap扩展性强太多了。

再来看看On-demand value computation,自定义构建元素。想象下面的场景,你要为一个查询学生信息的DAO增加结果缓存,并且结果超过60秒过期,我们可以用装饰模式结合MapMaker简单的实现。

Java代码 
interface StudentDao { 
    Information query(String name); 

 
class StudentDaoImpl implements StudentDao { 
    // 真正去查数据库的实现类 代码省略 

// 装饰器 
class CachedStudentDao implements StudentDao { 
    private final StudentDao studentDao; 
    private final ConcurrentMap<String, Information> cache; 
 
    public CachedStudentDao(final StudentDao studentDao) { 
        Preconditions.checkNotNull(studentDao, "studentDao"); 
        this.studentDao = studentDao; 
         
        this.cache = new MapMaker() // 构建一个 computingMap 
            .expiration(60, TimeUnit.SECONDS) // 元素60秒过期 
            .makeComputingMap(new Function<String, Information>(){ 
                @Override 
                public Information apply(String name) { 
                    return studentDao.query(name); 
                } 
            }); 
            // 传入匿名Function自定义缓存的初始化。如果缓存中没有name对应的数据,则调用真正的dao去数据库查找数据,同时缓存结果。 
    } 
 
    @Override 
    public Information query(String name) { 
        return cache.get(name); // 从computing cache中取结果 
    } 

 
public void test() { 
    StudentDao cachedStudentDao = new CachedStudentDao(studentDaoImpl); 
    // 装饰了studenDaoImpl的cachedStudentDao具备了缓存结果的能力。 



线程安全,高并发性能,元素过期都实现了,并且代码很简洁。多亏了MapMaker,脏活、累活,就交给它啦。不过要注意的是,要遵循ConcurrentHashMap的规范,其不允许有Null的Key和Value。如果查询出来的结果可能为Null的,可用简单的包装类包装一下,这里不给出代码了。

你可能感兴趣的:(java,Google)