[size=large][size=x-large]仔细研究了刚发布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被回收
}
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被回收
}
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;
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具备了缓存结果的能力。
}
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
的确, 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. [/size][/size]
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被回收
}
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被回收
}
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;
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具备了缓存结果的能力。
}
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
的确, 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. [/size][/size]