在系统设计的过程中经常使用本地缓存(ConcurrentHashMap实现),由于ConcurrentHashMap的特性,可以保证线程安全。通常缓存中的数据往往是读多写少的,ConcurrentHashMap是完完全全线程安全类,虽然相比较HashTable做了降低锁粒度的优化,但对于读请求是没有必要加锁。Doug Lea大神在设计concurrent包的时候为我们提供了一个CopyOnWriteArrayList,这个类通过读写分离的方式来实现线程安全,即读请求不加锁,写请求才加锁(典型的空间换时间实现)。这对于缓存来说是个很好的容器,不过很可惜JDK并未提供CopyOnWriteMap。这里自己简单模仿写了一个,希望在今后能有所帮助,类的实现如下:
package com.bj58.jyfz.fortunecheck.util; 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> extends HashMap<K,V> { private static final long serialVersionUID = 5209276986986262310L; /** * lock */ final transient ReentrantLock lock = new ReentrantLock(); /** * internal collection */ private transient volatile HashMap<K,V> map; /** * init capacity */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; public CopyOnWriteHashMap() { setMap(new HashMap<K,V>(DEFAULT_INITIAL_CAPACITY)); } 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; } @Override public int size() { return this.map.size(); } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean containsKey(Object key) { return this.map.containsKey(key); } @Override 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(); } } @Override public boolean containsValue(Object value) { return map.containsValue(value); } @Override public Set<K> keySet() { return map.keySet(); } @Override public Set<Entry<K, V>> entrySet() { return map.entrySet(); } @Override public Collection<V> values() { return map.values(); } @Override 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(); } } @Override 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(); } } @Override 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(); } } @Override 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(); } } @Override public V get(Object key) { return map.get(key); } @Override public V remove(Object 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(); } } }
这里面只重写了几个常用的方法,后续大家可以自行添加!这里强调一点,由于读请求不加锁,所以会有可能读取到脏数据,此情况发生在读写并发高的情况,通常都是毫秒级别的误差,对于缓存来说可以忽略了!