论坛首页 Java企业应用论坛

Google Collections(Guava)中强大的Concurrent MapMaker

浏览 23725 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-05-20   最后修改:2011-04-02
仔细研究了刚发布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.

	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。
	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简单的实现。

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

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

注:参考该文章了解是什么WeakReference?http://www.iteye.com/topic/401478
   发表时间:2010-05-20   最后修改:2010-05-20
的确,  google-collections 里有不少大招,  用的好即可以简化代码又可以提高性能,  比如以前的 best practice 说尽量使用 Collection 而非 List, Set 等, 现在 google collections 推荐尽量使用 Iterable, 因为很多时候超大量数据是一个 one-way "stream",  内存中不能存放如吃此之多的数据

google-collections 写道

Why so much emphasis on Iterators and Iterables?

In general, our methods do not require a Collection to be passed in when an Iterable or Iterator would suffice. This distinction is important to us, as sometimes at Google we work with very large quantities of data, which may be too large to fit in memory, but which can be traversed from beginning to end in the course of some computation. Such data structures can be implemented as collections, but most of their methods would have to either throw an exception, return a wrong answer, or perform abysmally. For these situations, Collection is a very poor fit; a square peg in a round hole.

An Iterator represents a one-way scrollable "stream" of elements, and an Iterable is anything which can spawn independent iterators. A Collection is much, much more than this, so we only require it when we need to.
0 请登录后投票
   发表时间:2010-05-20  
makeComputingMap 加匿名function那段.apply应该是线程安全的吧?
0 请登录后投票
   发表时间:2010-05-20  
不会是 Doug Lea 跑到 google 又去弄了一个 concurrent package 吧
0 请登录后投票
   发表时间:2010-05-20  
private static final transient Map<Class<?>, CacheBeanInfo> CACHE_BEANINFO = new ReferenceMap<Class<?>, CacheBeanInfo>(ReferenceKeyType.WEAK,ReferenceValueType.SOFT);

自己写的一个ReferenceMap,封装了ConcurrentHashMap。
其实有时候自己多留意一下多用心,很多东西并不是不可能。
在使用开源包的时候,还是要自己思考下如果是自己的话该如何实现。不然真成了只会找开源的人了。
0 请登录后投票
   发表时间:2010-05-21  
JE帐号 写道
makeComputingMap 加匿名function那段.apply应该是线程安全的吧?


当然了,如果不是线程安全的,岂能叫ConcurrentMap。
0 请登录后投票
   发表时间:2010-05-21  
beneo 写道
不会是 Doug Lea 跑到 google 又去弄了一个 concurrent package 吧



是的,里面的部分实现确实是Doug Lea贡献的。
0 请登录后投票
   发表时间:2010-05-21  
Agrael 写道
private static final transient Map<Class<?>, CacheBeanInfo> CACHE_BEANINFO = new ReferenceMap<Class<?>, CacheBeanInfo>(ReferenceKeyType.WEAK,ReferenceValueType.SOFT);

自己写的一个ReferenceMap,封装了ConcurrentHashMap。
其实有时候自己多留意一下多用心,很多东西并不是不可能。
在使用开源包的时候,还是要自己思考下如果是自己的话该如何实现。不然真成了只会找开源的人了。


请原谅我的愚钝,这样一行声明不知道想要表达什么意思?
0 请登录后投票
   发表时间:2010-05-21  
Norther 写道
Agrael 写道
private static final transient Map<Class<?>, CacheBeanInfo> CACHE_BEANINFO = new ReferenceMap<Class<?>, CacheBeanInfo>(ReferenceKeyType.WEAK,ReferenceValueType.SOFT);

自己写的一个ReferenceMap,封装了ConcurrentHashMap。
其实有时候自己多留意一下多用心,很多东西并不是不可能。
在使用开源包的时候,还是要自己思考下如果是自己的话该如何实现。不然真成了只会找开源的人了。


请原谅我的愚钝,这样一行声明不知道想要表达什么意思?


呵呵,可能是我说得不名不白。ReferenceKeyType.WEAK,ReferenceValueType.SOFT构造函数传了这2个枚举进去,KEY为弱引用,VALUE为软引用,就是这个意思。满足你说的那种功能。
0 请登录后投票
   发表时间:2010-05-21  
Agrael 写道
Norther 写道
Agrael 写道
private static final transient Map<Class<?>, CacheBeanInfo> CACHE_BEANINFO = new ReferenceMap<Class<?>, CacheBeanInfo>(ReferenceKeyType.WEAK,ReferenceValueType.SOFT);

自己写的一个ReferenceMap,封装了ConcurrentHashMap。
其实有时候自己多留意一下多用心,很多东西并不是不可能。
在使用开源包的时候,还是要自己思考下如果是自己的话该如何实现。不然真成了只会找开源的人了。


请原谅我的愚钝,这样一行声明不知道想要表达什么意思?


呵呵,可能是我说得不名不白。ReferenceKeyType.WEAK,ReferenceValueType.SOFT构造函数传了这2个枚举进去,KEY为弱引用,VALUE为软引用,就是这个意思。满足你说的那种功能。



那样的话,确实不错呢,有代码吗?学习学习。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics