自定义CopyOnWriteHashMap

    在系统设计的过程中经常使用本地缓存(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,使用前请慎重考虑具体场景!

原文地址:https://www.cnblogs.com/1ning/p/8479680.html