JUC-多线程(4.集合类安全性)学习笔记

集合类的安全性及解决

1. List

  1. 举例
public class JUC05_NotSafeDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        //方案 1
//        List<String> list = new Vector<>();
        //方案 2
//        List<String> list = Collections.synchronizedList(new ArrayList<>());
        //方案 3
//        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 30; i++){
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }

}
  1. 故障现象
  • 并发修改异常
java.util.ConcurrentModificationException
  1. 原因
  • 多个线程几乎同时对一个 List 进行操作,相互争抢,List 承受不了
  1. 解决方案
  • 方案 1

    • 把 ArrayList 换成 List list = new Vector<>();即可
    • 因为 ArrayList 线程不安全,Vector 线程安全
      • Vector 底层的 add 方法已经加了 synchronized 同步锁
      • 使用 Vector 数据一致性增强,访问性能下降
  • 方案 2

    • List list = Collections.synchronizedList(new ArrayList<>());
    • 在数据量小的时候,可以使用
    • 扩展
      • HashMap、HashSet 也都是线程不安全
  • 方案 3 ⭐
    在这里插入图片描述

    • List list = new CopyOnWriteArrayList<>();(写时复制技术)
    • 写时复制原理:
      • 第一个线程来了,把(原本的资源类)(1.0)复制一份,自己去写
      • 原本的资源类,供其他线程读
      • 第一个线程在写的时候,对复制的那一份加锁,写完了解锁,用(更新的资源类)(2.0)替换原本的
      • 第二个线程,就复制(2.0)版本的资源类写
      • 。。。
    • 源码
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

  • 分析
    • CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy (使用 Arrays.copyof() 进行扩容) ,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。
    • 添加元素后,再将原容器的引用指向新的容器setArray(newElements)。
    • 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
    • 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

2. Set

  1. 解决方案
  • 方案 1

    • Set set = Collections.synchronizedSet(new HashSet<>());
    • 在数据量小的时候,可以使用
  • 方案 2

    • Set set = new CopyOnWriteArraySet<>();(写时复制技术)
  1. 补充
  • HashSet 底层是 HashMap
  • HashSet 的 add 就是调用的 HashMap 的 push ,不过就是把 Value 设为一个固定的 Object 类型的常量
  • private static final Object PRESENT = new Object();

3. Map

  1. 解决方案
  • 方案 1
    • Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
  • 方案 2
    • Map<String,String> map = new ConcurrentHashMap<>();//线程安全
  1. 补充
  • HashMap 底层是 Node类型的数组 + 链表 + 红黑树

4. 优化建议

  1. 使用 Map 时 ,按照具体业务的需求把容量设置为一个较大的值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuan_404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值