欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

集合不安全

程序员文章站 2022-07-15 09:25:38
...

集合不安全

​ 大家很容易知道哪些集合安全哪些不安全,但是很多时候都是局限于知道,而不清楚细节。所以这就是展示一些细节的地方。

先说结论:

List、Set、Map都不安全

以List为例

private static void ShowList(){    
    //看源码    
    List<String> list = new ArrayList<>();    
     
    list.add("a");    
    list.add("a");    
    list.add("a");    
    //consumer接口    
    //list.forEach(System.out::println);    
    for (int i = 0; i < 3; i++) {        
        new Thread(() -> {            
            list.add(UUID.randomUUID().toString().substring(0, 8));            
            System.out.println(list);        
        }, String.valueOf(i)).start();    
    }
}

​ 这段代码就表现出了List不安全的性质,将会出现一个异常

java.util.ConcurrentModificationException

​ 原因是: 迭代器的modCount和expectedModCount的值不一致

​ 就是线程安全异常。

​ 不要停留在知道List不安全,要能说能写出这段代码。

​ 其中注释的地方有用到接口consumer,简单理解就是lambda表达式简写。明显能看出将foreach循环压缩到了一行。

寻找原因,即找源码,看是否有synchronized 修饰

​ 出现了问题就要解决,共有三种解决方案:

//3.1 List<String> list = new Vector<>();
//3.2 List<String> list = Collections.synchronizedList(new LinkedList<>());
//3.3 List<String> list1 = new CopyOnWriteArrayList<>();

第一种是用Vector,第二种是用Collections工具类的方法,第三种用写时复制方法。
ArrayList底层Object[]数组、Set底层HashMap、HashMap底层链表散列
Set和List类似

Map则是特殊的

//Map<String, String> map = new ConcurrentHashMap<>();

最后说一下简单说下写时复制:

​ 写入时复制是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。

​ 此作法的主要优点是如果调用者没有修改该资源,就不会有副本(private copy)被建立,因此多个调用者只是读取操作是可以共享同一份资源。

​ 就是资源A 要被修改时创建一个副本A+,修改时其他进程看的仍是原资源A,当修改完后,引用指向资源A+,A被销毁。确保了在修改时数据的安全。

​ 总结就是确保了线程安全同时保障了效率。