DK1.5 引入了 concurrent package, 提供了更多的concurrent 控制方法。 还提供了一个 ConcurrentHashMap 类。从API上看,是可以读写同步。多个thread可以同时读取,一个thread写的时候,其他thread都不能读写。 这是一个用处很广、很方便的类。我想,能不能在 jdk1.4 及以下版本也提供一个。于是查看了 ConcurrentHashMap的代码。 我本以为,实现思路应该是用到了 ReadWriteLock. 大致是这样的思路。
Java代码
// my guess
class CocurrentHashMap
{
Private Map map = null;
final ReadWriteLock rwLock = new …. ;
final Lock readLock = rwLock.readLock();;
final Lock writeLock = rwLock.writeLock();;
// decorate the map as concurrent
public CocurrentHashMap(Map m);{
map = m;
}
// all write method, like put, putAll, remove, clear
public void putAll(Map m);{
writeLock.lock();;
try{
map.putAll(m);;
}finally{
writeLock.unlock();;
}
}
// all read method. like get, containsKey, containsValue, entrySet();
public Object get(Object key);{
readLock.lock();;
try{
return map.get(key);;
}finally{
readLock.unlock();;
}
};
// as we can see, in such places it is convenient to use AOP here. :-);
看了java 1.5 code,才知道不是这样。ConcurrentHashMap是一个比较复杂的类,自己实现了Lock。
不过也无妨,上面的思路很简单,我也可以找到 third party concurrent.jar, 按照上面自己的猜测思路为 jdk1.4 (and below) 写一个concurrent map.
这时候,我又想到。Read Lock也是lock。而某些情况下,只是初始化或者 refresh的时候,map 需要write,大多数情况下,都是 read。能不能继续减少 Read Lock 的 overhead?
我采取了这样一个思路。维护两个 map. 一个是 map to read, read only. 一个是 map to write, write only.
当用户使用 read method 的时候,就用 map to read ; 当用户使用 write method的时候,就写入到 map to write,之后还要copy 一份map to write ,然后把这个copy 赋给map to read。
所以,read 的时候,非常快,几乎没有 overhead。而write的时候,非常 expensive, 每次write完之后,都要 copy 一遍。所以,建议尽量使用 putAll() method。
这个思路还有一点不利,就是 同时维护两个同样的entry 的 map. 空间上的效率不是很好,虽然,key, value pair都是object reference, 但map 的entry set结构要有两份。
代码如下。为了效率,没有用AOP。:-)
Java代码
?
/*
* Read Write Map
*
* Write is expensive.
* Read is fast as pure HashMap.
*
* Note: extra info is removed for free use
*/
package net.sf.map;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.Collections;
public class ReadWriteMap implements Map {
protected volatile Map mapToRead = Collections.unmodifiableMap(getNewMap(););;
protected final Map mapToWrite = Collections.synchronizedMap(getNewMap(););;
// you can override it as new TreeMap();;
protected Map getNewMap();{
return new HashMap();;
}
// copy mapToWrite to mapToRead
protected void sync();{
Map newMapToRead = getNewMap();;
newMapToRead.putAll(mapToWrite);;
mapToRead = Collections.unmodifiableMap(newMapToRead);;
}
// read methods
public int size(); {
return mapToRead.size();;
}
public boolean isEmpty(); {
return mapToRead.isEmpty();;
}
public boolean containsKey(Object key); {
return mapToRead.containsKey(key);;
}
public boolean containsValue(Object value); {
return mapToRead.containsValue(value);;
}
public Collection values(); {
return mapToRead.values();;
}
public Set entrySet(); {
return mapToRead.entrySet();;
}
public Set keySet(); {
return mapToRead.keySet();;
}
public Object get(Object key); {
return mapToRead.get(key);;
}
// write methods
public void clear(); {
mapToWrite.clear();;
sync();;
}
public Object remove(Object key); {
Object o = mapToWrite.remove(key);;
sync();;
return o;
}
public Object put(Object key, Object value); {
Object o = mapToWrite.put(key, value);;
sync();;
return o;
}
public void putAll(Map t); {
mapToWrite.putAll(t);;
sync();;
}
}
希望这段代码能够帮助 有类似需求的人。