zoukankan      html  css  js  c++  java
  • 集合类线程安全问题

    涉及到的常用类

    • ArrayList
    • HashSet
    • HashMap

    在多线程下比较容易出现的异常是 java.util.ConcurrentModificationException,也就是并发修改异常。这个是由于并发争抢修改导致写入数据中断,数据出现异常

    案例演示

    List<String> list = new ArrayList<>();
            
    for (int i = 0; i < 30; i++) {
                
        new Thread(() -> {
                    
            list.add(UUID.randomUUID().toString().substring(0,6));
                    
            System.out.println(list);
                
        }, "thread" + i).start();
            
    }

     故障现象

     java.util.ConcurrentModificationException

    解决方案

     方案一:使用线程安全的list类

     List<String> list = new Vector<>();

    方案二:使用集合工具类

    List<String> list = Collections.synchronizedList(new ArrayList<>());

    方案三:使用写时复制 CopyOnWriteArrayList

    List<String> list = new CopyOnWriteArrayList<>();

    优化建议

    采用方案三。

    理由:

    Vector和工具类都是使用了同步锁,效率低下,而CopyOnWriteArrayList则不同,可以看一下源码

    从源码可以看出,这里使用了可重入锁,其效率高于synchronized。其次,这里使用的是写时复制,是将当前的容器Object[ ] elements进行copy,其实底层就是数组的复制,复制出一个新的容器Object[ ] newElements,然后向新的容器中添加元素,添加完元素后,将原容器的引用指向新的容器,setArray(newElements)。这样做的好处是可以并发读而不需要加锁,因为当前的元素不需要添加元素,所有CopyOnWriteArrayList也是一种读写分离的思想,读和写是不同的容器。

    剩下的HashSet和HashMap也是类似的解决方案。例如:

    HashSet

        private static void setUnSafeCase() {
    //        Set<String> set = new HashSet<>();
    //        Set<String> set = Collections.synchronizedSet(new HashSet<>());
            Set<String> set = new CopyOnWriteArraySet<>();
            for (int i = 0; i < 30; i++) {
                new Thread(() -> {
                    set.add(UUID.randomUUID().toString().substring(0,6));
                    System.out.println(set);
                }, "thread" + i).start();
            }
        }

    CopyOnWriteArraySet 其实就是使用的CopyOnWriteArrayList

    HashMap

        private static void mapUnSafeCase() {
    //        Map<String, String> map = new HashMap<>();
    //        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
            Map<String, String> map = new ConcurrentHashMap<>();
            for (int i = 0; i < 30; i++) {
                new Thread(() -> {
                    map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,6));
                    System.out.println(map);
                }, "thread" + i).start();
            }
        }

    这里的ConcurrentHashMap在1.7使用的分段锁,1.8又改成了红黑树,这里不再赘述,详见ConcurrentHashMap

  • 相关阅读:
    Python修饰器 ,控制授权,通过ini配置文件 使用密钥 给函数限制试用期和过期后试用次数
    excel vba 自定义函数 使用正则表达式提取字符串
    python 值比较判断,np.nan is np.nan 却 np.nan != np.nan ,pandas 单个数据框/单元格 值判断nan
    python 读取中文CSV 'gbk' codec can't decode bytes in position 2-3:illegal multibyte sequence
    python ipython [Errno 22] invalid mode ('rb') or filename 、IDE工作路径
    windows下 python 添加PYTHONPATH 环境变量
    pandas(python2) 读取中文数据,处理中文列名
    qq邮箱 微信提醒不通知
    python :import error
    python推荐淘宝物美价廉商品 2.0
  • 原文地址:https://www.cnblogs.com/weianlai/p/14589514.html
Copyright © 2011-2022 走看看