线程安全的HashMap

 一、一般模式下线程安全的HashMap

  默认情况常用的HashMap都是线程不安全的,在多线程的环境下使用,常常会造成不可预知的,莫名其妙的错误。那么,我们如何实现一个线程安全的HashMap呢?其中一个可行的方式是使用Collectons.synchronizedMap() 方法来包装我们的HashMap。如下:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String,String>());

  

Collections.synchronizedMap()会生成一个SynchronizedMap,它使用委托模式,将自己HashMap相关的功能交给传入HashMap实现,二自己负责线程安全的相关实现,下面看看
SynchronizedMap的定义:
    private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map

        // 使用 mutex 实心对 map 的互斥操作
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

  如在代码中看到的,所有对Map的操作都需要用 这个 mutex  来同步,以实现线程安全。比如说下面这些常见的对HashMap的操作方法:

        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

  除了以上看到的方法之外,其他的Map相关的方法有类似的操作。虽然这个包装的Map可以实现线程安全的要求,但是,它在多线程环境下的性能表现并不是很好,无论是对Map的读取还是写入,偶数需要获得 mutex 的同步锁,这会导致所有对Map的安全操作也会进入等待状态,知道mutex可用。 如果并发级别不高,那么这个 包装的Map可以基本满足要求,但是在搞并发的环境中,我们需要寻找新的解决方案。 ——---> 那就是我们的 ConcurrentHashMap.

 二、提高"锁"性能的策略

  1. 减少锁的持有时间

    只在必要时进行同步,减少锁的持有时间。比如说在一个方法中只有一个变量需要同步,那么就没有必要对这整个方法都进行同步,而只需要同步这个变量即可。

        // 无谓的加锁时间
        public synchronied void syncMethod() {

                othrerMethod();
                mutexMethod();
                otherMethod();
        }

        // 正确的加锁时间
        public  void syncMethod() {

                othrerMethod();
                synchronied(this){
                mutexMethod();
                }
                otherMethod();
        }

  2.  减小锁的粒度

    在获取全局信息方法不频繁的时候,通过减小锁的粒度可以搞系统的吞吐量。

  3. 读写分离锁替换独占锁

    在读都写少的情况下,使用读写分离锁,多线程读时不阻塞,而只对写线程进行同步。

  4. 锁分离

    对不同功能的锁进行不同的锁策略。

  5. 锁粗化

    系统对于"锁"的调度也是需要性能消耗的,又是我们可以适当的加大锁的范围,比如说在循环中尽量减少对锁的请求和释放,而是在得到锁的情况,一次性把问题解决。

  

猜你喜欢

转载自www.cnblogs.com/ytuan996/p/10585633.html