5_集合的线程安全

Posted root_zhb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5_集合的线程安全相关的知识,希望对你有一定的参考价值。

1、ArrayList

1.1、线程不安全演示

public class NotSafeDemo {
    //多个线程同时对集合进行修改
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }, "线程" + i).start();
        }
    }
}

报错:java.util.ConcurrentModificationException

1.2、解决方案-Vector

List list = new Vector();

1.3、解决方案-Collections

List list= Collections.synchronizedList(new ArrayList());

1.4、解决方案-CopyOnWriteArrayList

写时复制技术

List list=new CopyOnWriteArrayList<>();
  1. 特性:

    • 它最适合于具有以下特征的应用程序:List 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
    • 它是线程安全的。
    • 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
    • 迭代器支持 hasNext(), next()等不可变操作,但不支持可变 remove()等操作。
    • 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
  2. 总结:

    • 独占锁效率低:采用读写分离思想解决

    • 写线程获取到锁,其他写线程阻塞

    • 复制思想
      当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。

      这时候会抛出来一个新的问题,也就是数据不一致的问题。如果写线程还没来得及写会内存,其他的线程就会读到了脏数据。

  3. “动态数组”机制

    • 它内部有个“volatile 数组”(array)来保持数据。在“添加/修改/删除”数据时,都会新建一个数组,并将更新后的数据拷贝到新建的数组中,最后再将该数组赋值给“volatile 数组”, 这就是它叫做 CopyOnWriteArrayList 的原因
    • 由于它在“添加/修改/删除”数据时,都会新建数组,所以涉及到修改数据的操作,CopyOnWriteArrayList 效率很低;但是单单只是进行遍历查找的话,效率比较高。
  4. “线程安全”机制

    • 通过 volatile 和互斥锁来实现的。
    • 通过“volatile 数组”来保存数据的。一个线程读取 volatile 数组时,总能看到其它线程对该 volatile 变量最后的写入;就这样,通过 volatile 提供了“读取到的数据总是最新的”这个机制的保证。
    • 通过互斥锁来保护数据。在“添加/修改/删除”数据时,会先“获取互斥锁”,再修改完毕之后,先将数据更新到“volatile 数组”中,然后再“释放互斥锁”,就达到了保护数据的目的。

2、HashSet

2.1、线程不安全演示

public class NotSafeDemo {
    //多个线程同时对集合进行修改
    public static void main(String[] args) {
        Set<String> set=new HashSet<>();
        for(int i=0;i<100;i++){
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            }).start();
        }
    }
}

2.2、解决方案CopyOnWriteArraySet

Set<String> set=new CopyOnWriteArraySet<>();

3、HashMap

3.1、线程不安全演示

public class NotSafeDemo {
    //多个线程同时对集合进行修改
    public static void main(String[] args) {
        Map<String,String> map=new HashMap<>();
        for(int i=0;i<100;i++){
            String key=String.valueOf(i);
            new Thread(()->{
                map.put(key,UUID.randomUUID().toString().substring(0,8));
                System.out.println(map);
            }).start();
        }
    }
}

3.2、解决方案ConcurrentHashMap

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

以上是关于5_集合的线程安全的主要内容,如果未能解决你的问题,请参考以下文章

线程同步-使用ReaderWriterLockSlim类

线程安全集合

Java中线程安全的集合浅析

ConcurrentQueue表示线程安全的先进先出 (FIFO) 集合。

Java集合_学习笔记

Java并发编程:多线程环境中安全使用集合API(含代码)