zoukankan      html  css  js  c++  java
  • 自定义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,使用前请慎重考虑具体场景!

  • 相关阅读:
    数据系统与分布式(二) 分布式数据系统(复制与分片)
    数据系统和分布式(一)数据系统基础
    可执行文件(ELF)的装载与进程
    HTTPS协议
    后台开发 缓存, 数据库, 高并发等等
    Golang中new和make的区别
    吴恩达:机器学习里面的作业1遇到的
    笔记——操作系统导论:环境配置
    笔记——操作系统导论:第二章
    Games 101 作业1代码解析
  • 原文地址:https://www.cnblogs.com/1ning/p/8479680.html
Copyright © 2011-2022 走看看