zoukankan      html  css  js  c++  java
  • Java多线程_并发容器ConcurrentHashMap/CopyOnWriteArrayList/CopyOnWriteArraySet

    ConcurrentHashMap
            HashMap是线程不安全的,可以使用Collections.synchronizedMap(map)把一个不安全的map变成安全的,但是这里可以直接使用ConcurrentHashMap。
           ConcurrentHashMap是线程安全的的Hash表。对于多线程的操作,介于HashMap和HashTable之间。内部采用“锁分段”机制替代HashTable的独占锁,进而提高性能。
             一个ConcurrentHashMap由多个segment【默认16个】组成,每一个segment都包含了一个HashEntry数组的HashTable, 每一个segment包含了对自己的HashTable的操作,比如get,put,replace等操作,这些操作发生的时候,对自己的HashTable进行锁定。由于每一个segment写操作只锁定自己的HashTable,所以可能存在多个线程同时写的情况,性能无疑好于只有一个HashTable锁定的情况。
    示例:
    ConcurrentHashMap和HashMap在迭代过程中添加键值对的比较。

    ConcurrentHashMap代码:

    import java.util.Iterator;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    public class ConcurrentHashMapDemo {
        public static void main(String[] args) {
            ConcurrentMap<String, String> cmap = new ConcurrentHashMap<>();
            cmap.put("1", "111");
            cmap.put("2", "222");
            cmap.put("3", "333");
            cmap.put("4", "444");
            System.out.println("原Map:" + cmap);
            Iterator<String> iterator = cmap.keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                if (key.equals("2")) {
                    cmap.put("new" + key, "new222");
                }
            }
            System.out.println("修改后:" + cmap);
        }
    }

    结果:

    HashMap代码:

    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    public class HashMapDemo {
        public static void main(String[] args) {
            Map<String, String> map = new HashMap<>();
            map.put("1", "111");
            map.put("2", "222");
            map.put("3", "333");
            map.put("4", "444");
            System.out.println("原Map:" + map);
            Iterator<String> it = map.keySet().iterator();
            while (it.hasNext()) {
                String key = it.next();
                if (key.equals("2")) {
                    map.put("new" + key, "new222");
                }
            }
            System.out.println("修改后:" + map);
        }
    }

    结果:

    查看输出,很明显ConcurrentHashMap可以支持向map中添加新元素,而HashMap则抛出了ConcurrentModificationException。查看异常堆栈记录,可以发现是下面这条语句抛出异常:

    String key = it.next();

    这就意味着新的元素在HashMap中已经插入了,但是在迭代器执行时出现错误。事实上,集合对象的迭代器提供快速失败(Fail-Fast)的机制,即修改集合对象结构或者元素数量都会使迭代器触发这个异常。

    使用要点简单总结:
    1 get方法不涉及到锁,也就是说获得对象时没有使用锁;
    2、put、remove方法要使用锁,但并不一定有锁争用,原因在于ConcurrentHashMap将缓存的变量分到多个Segment,每个Segment上有一个锁,只要多个线程访问的不是一个Segment就没有锁争用,就没有堵塞,各线程用各自的锁,ConcurrentHashMap缺省情况下生成16个Segment,也就是允许16个线程并发的更新而尽量没有锁争用;
    3、Iterator对象的使用,不一定是和其它更新线程同步,获得的对象可能是更新前的对象,ConcurrentHashMap允许一边更新、一边遍历,也就是说在Iterator对象遍历的时候,ConcurrentHashMap也可以进行remove,put操作,且遍历的数据会随着remove,put操作产出变化。

    CopyOnWriteArrayList
           ArrayList是线程不安全的,也可以使用 Collections.synchronizedList(list)变成线程安全的。这里也可以直接使用CopyOnWriteArrayList。
           CopyOnWriteArrayList是线程安全的,它内部持有了ReenTrantLock对象,它使用了一种叫写时复制的方法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。而读的时候不加锁不阻塞,即在原版上读,这样就实现了读写的分离。
    示例:
    这里主要测试它的线程安全性,不再与ArrayList作比较。

    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class CopyOnWriteArrayListDemo {
        static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        static {
            list.add("111");
            list.add("222");
            list.add("333");
            list.add("444");
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            for (int i = 0; i < 5; i++) {
                new Thread(test).start();
            }
        }
    
        static class Test implements Runnable {
    
            @Override
            public void run() {
                synchronized (this) {
    
                    Iterator<String> it = list.iterator();
                    while (it.hasNext()) {
                        System.out.print(it.next() + " ");
                    }
                    System.out.println();
                    list.add("555");
                }
            }
        }
    }

    结果:

    CopyOnWriteArraySet
            CopyOnWriteArraySet与CopyOnWriteArrayList的用法基本相同,唯一注意的就是CopyOnWriteArraySet是不允许重复(唯一)的。
    示例:
    我们修改一下CopyOnWriteArrayList的示例来看一下

    import java.util.Iterator;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    public class CopyOnWriteArraySetDemo {
        static CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
        static {
            set.add("111");
            set.add("222");
            set.add("222");
            set.add("333");
        }
    
        public static void main(String[] args) {
            Test test = new Test();
            for (int i = 0; i < 5; i++) {
                new Thread(test).start();
            }
        }
    
        static class Test implements Runnable {
    
            @Override
            public void run() {
                synchronized (this) {
    
                    Iterator<String> it = set.iterator();
                    while (it.hasNext()) {
                        System.out.print(it.next() + " ");
                    }
                    System.out.println();
                    set.add("444");
                }
            }
        }
    }

    结果:

  • 相关阅读:
    CodeDeploy 应用程序规范文件
    Lambda 函数的最佳实践
    路由策略
    AWS CodeStar
    使用 Lambda@Edge 在边缘站点自定义内容
    Step Functions
    将应用程序部署到 AWS Elastic Beanstalk 环境
    DynamoDB 静态加密
    web数据安全——防篡改
    【Spring】spring全局异常处理即全局model数据写入
  • 原文地址:https://www.cnblogs.com/ericz2j/p/10308077.html
Copyright © 2011-2022 走看看