Google Collections中强大的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代码 复制代码
  1. public void testWeakKeys() throws Exception {   
  2.     ConcurrentMap<Key, Value> map = new MapMaker()   
  3.         .weakKeys() // 指定Map保存的Key为WeakReference机制   
  4.         .makeMap();    
  5.   
  6.     Key key = new Key();   
  7.     map.put(key, new Value()); // 加入元素   
  8.     key = null// key变成了WeakReference   
  9.   
  10.     System.gc();// 触发垃圾回收   
  11.     TimeUnit.SECONDS.sleep(1L);   
  12.   
  13.     assertTrue(map.isEmpty()); // map空了,因为WeakReference被回收   
  14. }  
	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代码 复制代码
  1. public void testWeakValues() throws Exception {   
  2.     ConcurrentMap<Key, Value> map = new MapMaker()   
  3.         .weakValues() // 指定Map保存的Value为WeakReference机制   
  4.         .makeMap();    
  5.   
  6.     Key key = new Key();   
  7.     Value value = new Value();   
  8.     map.put(key, value); // 加入元素   
  9.     key = null// Key成了WeakReference   
  10.        
  11.     System.gc();// 触发垃圾回收   
  12.     TimeUnit.SECONDS.sleep(1L);   
  13.        
  14.     assertFalse(map.isEmpty()); // map里的东西还在,因为Value还是StrongReference   
  15.        
  16.     value = null// 这次value也变成了WeakReference   
  17.   
  18.     System.gc(); // 触发垃圾回收   
  19.     TimeUnit.SECONDS.sleep(1L);   
  20.   
  21.     assertTrue(map.isEmpty()); // map真空了,因为Value是WeakReference被回收   
  22. }  
	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代码 复制代码
  1. interface StudentDao {   
  2.     Information query(String name);   
  3. }   
  4.   
  5. class StudentDaoImpl implements StudentDao {   
  6.     // 真正去查数据库的实现类 代码省略   
  7. }   
  8. // 装饰器   
  9. class CachedStudentDao implements StudentDao {   
  10.     private final StudentDao studentDao;   
  11.     private final ConcurrentMap<String, Information> cache;   
  12.   
  13.     private CachedStudentDao(final StudentDao studentDao) {   
  14.         Preconditions.checkNotNull(studentDao, "studentDao");   
  15.         this.studentDao = studentDao;   
  16.            
  17.         this.cache = new MapMaker() // 构建一个 computingMap   
  18.             .expiration(60, TimeUnit.SECONDS) // 元素60秒过期   
  19.             .makeComputingMap(new Function<String, Information>(){   
  20.                 @Override  
  21.                 public Information apply(String name) {   
  22.                     return studentDao.query(name);   
  23.                 }   
  24.             });   
  25.             // 传入匿名Function自定义缓存的初始化。如果缓存中没有name对应的数据,则调用真正的dao去数据库查找数据,同时缓存结果。   
  26.     }   
  27.   
  28.     @Override  
  29.     public Information query(String name) {   
  30.         return cache.get(name); // 从computing cache中取结果   
  31.     }   
  32. }   
  33.   
  34. public void test() {   
  35.     StudentDao cachedStudentDao = new CachedStudentDao(studentDaoImpl);   
  36.     // 装饰了studenDaoImpl的cachedStudentDao具备了缓存结果的能力。   
  37. }  
	interface StudentDao {
		Information query(String name);
	}
	
	class StudentDaoImpl implements StudentDao {
		// 真正去查数据库的实现类 代码省略
	}
	// 装饰器
	class CachedStudentDao implements StudentDao {
		private final StudentDao studentDao;
		private final ConcurrentMap<String, Information> cache;

		private 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的,可用简单的包装类包装一下,这里不给出代码了。

怎么样?你是不是心动了呢?快下载来看看吧。

注:参考该文章了解是什么WeakReference?http://www.javaeye.com/topic/401478

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值