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

  • 相关阅读:
    for() 和$.each()的用法区别
    HTML5 UTF-8 中文乱码
    chrome,opera..通过file协议浏览html代码时,发送的ajax请求本地文件,会报跨域错误
    局部方法$("html").load()和全局方法$.get()、$.post()
    iOS开发--动画篇之layout动画深入
    iOS 开发--转场动画
    iOS开发--泛型
    Python十分钟学会
    iOS 开发--动画
    一个简单的ObjC和JavaScript交互工具
  • 原文地址:https://www.cnblogs.com/weianlai/p/14589514.html
Copyright © 2011-2022 走看看