在系统设计的过程中经常使用本地缓存(ConcurrentHashMap实现),由于ConcurrentHashMap的特性,可以保证线程安全。通常缓存中的数据往往是读多写少的,ConcurrentHashMap是完完全全线程安全类,虽然相比较HashTable做了降低锁粒度的优化,但对于读请求是没有必要加锁。Doug Lea大神在设计concurrent包的时候为我们提供了一个CopyOnWriteArrayList,这个类通过读写分离的方式来实现线程安全,即读请求不加锁,写请求才加锁(典型的空间换时间实现)。这对于缓存来说是个很好的容器,不过很可惜JDK并未提供CopyOnWriteMap。这里自己简单模仿写了一个,希望在今后能有所帮助,类的实现如下:
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zhangyining on 18/2/27 027.
*/
public class CopyOnWriteHashMap<K,V> implements Serializable, Cloneable {
private static final long serialVersionUID = 5209276986986262310L;
/**
* lock
*/
private final transient ReentrantLock lock = new ReentrantLock();
/**
* internal collection
*/
private transient volatile HashMap<K,V> map;
/**
* init capacity
*/
private static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
/**
* max capacity
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
public CopyOnWriteHashMap() {
setMap(new HashMap<>(DEFAULT_INITIAL_CAPACITY));
}
public CopyOnWriteHashMap(int initialCapacity) {
if (initialCapacity < 0){
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
}
if (initialCapacity > MAXIMUM_CAPACITY){
initialCapacity = MAXIMUM_CAPACITY;
}
setMap(new HashMap<>(initialCapacity));
}
public CopyOnWriteHashMap(HashMap<? extends K, ? extends V> m) {
HashMap<K,V> map = new HashMap<>(m);
setMap(map);
}
final HashMap<K,V> getMap() {
return map;
}
final void setMap(HashMap<K,V> map) {
this.map = map;
}
public int size() {
return this.map.size();
}
public boolean isEmpty() {
return size() == 0;
}
public boolean containsKey(K key) {
return map.containsKey(key);
}
public void clear() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
newMap.clear();
map = newMap;
} finally {
lock.unlock();
}
}
public boolean containsValue(V value) {
return map.containsValue(value);
}
public Set<K> keySet() {
return map.keySet();
}
public Set<Map.Entry<K, V>> entrySet() {
return map.entrySet();
}
public Collection<V> values() {
return map.values();
}
public V replace(K key, V value) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
V val = newMap.replace(key, value);
map = newMap;
return val;
} finally {
lock.unlock();
}
}
public boolean replace(K key, V oldValue, V newValue) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
boolean flag = newMap.replace(key, oldValue, newValue);
map = newMap;
return flag;
} finally {
lock.unlock();
}
}
public V put(K key, V value) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
V val = newMap.put(key, value);
map = newMap;
return val;
} finally {
lock.unlock();
}
}
public void putAll(Map<? extends K, ? extends V> m) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
newMap.putAll(m);
map = newMap;
} finally {
lock.unlock();
}
}
public V get(K key) {
return map.get(key);
}
public V remove(K key) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
HashMap<K,V> newMap = new HashMap<>(map);
V val = newMap.remove(key);
map = newMap;
return val;
} finally {
lock.unlock();
}
}
}
该类并没有按照JDK集合的方式来创建,后续有时间结合Collection补齐接口和抽象类
二者对比
类 | 一致性 | 效率 | 内存占用 | 适用情况 |
ConcurrentHashMap | 强一致性 | 较高 | 一般 | 无要求 |
CopyOnWriteHashMap | 最终一致性 | 高 | 双倍内存 | 读多写少 |
这里强调一点,由于读请求不加锁,所以会有可能读取到脏数据,此情况发生在读写并发高的情况,通常都是毫秒级别的误差,对于数据时效性严格要求的场景不适用,且对于写数据较多的场景会经常触发GC,使用前请慎重考虑具体场景!