• [集合]线程安全的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. 锁粗化

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

      

  • 相关阅读:
    tabhost切换标签:Log中出现You must supply a layout_width attribute的解决方法
    listview去掉底部多出的边框黑色
    使用fragmenttabhost后,子fragment怎么获取ID?怎么用getSharedPreferences
    android用shape给linearLayout设置边框,怎样只保留底部或顶部的边框,把其它三个方向的边框去掉呢?
    linux删除文件未释放空间问题处理
    mount: unknown filesystem type 'LVM2_member'解决方案【转】
    centos系统lvm的安装
    一个或多个音频服务未运行 win7 错误1079:此服务的账户不同于运行于同一进程上的其他服务账户
    php SimpleXML
    new JSONObject(str)无法解析 报错:org.json.JSONException: Value of type java.lang.String cannot be converted to JSONObject
  • 原文地址:https://www.cnblogs.com/ytuan996/p/10585633.html
Copyright © 2020-2023  润新知