JUC系列(三) 不安全的集合类

简介: 在多线程的情况下 我们常用的一些集合并不能保持线程的安全 那么我们该怎么办呢
📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务数据结构netty单点登录SSMSpringCloudAlibaba
😝公众号😝想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
当前专栏JUC系列

集合类不安全

List不安全

并发 arrayList是不安全的
解决方案:

1. vector 线程安全,因为新增方法前 带了 sync关键字,这个方法并不是最优解
2. 集合工具类 Collections.synchronizedList(new ArrayList<>());
3. JUC包下 CopyOnWriteArrayList<>()

CopyOnWrite 写入时复制,COW 计算机程序设计领域的一种优化策略

如 : 比如多个调用者调用同一个list,读取的时候,固定的,写入(覆盖)

在写入的时候 避免覆盖,造成数据问题; 读写分离

问题: 为什么不用vector 为什么呢?

答 : 因为只要使用 sync关键字,效率都会低一些,而CopyOnWriteArrayList,底层的方法是用的lock锁

//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        //并发 arrayList是不安全的
        /*  解决方案
         * 1. vector 线程安全,因为新增方法前 带了 sync关键字,这个方法并不是最优解
         * 2. 集合工具类 Collections.synchronizedList(new ArrayList<>());
         * 3. JUC包下 CopyOnWriteArrayList<>()
         *  CopyOnWrite 写入时复制,COW 计算机程序设计领域的一种优化策略
         * 如 : 比如多个调用者调用同一个list,读取的时候,固定的,写入(覆盖)
         * 在写入的时候 避免覆盖,造成数据问题;
         * 读写分离
         *
         * 问题: 为什么不用vector 为什么呢? 因为只要使用 sync关键字,效率都会低一些,而CopyOnWriteArrayList,底层的方法是用的lock锁
         * */
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 600; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}
写入时复制的add方法的原理
    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();//释放锁
        }
    }
Set不安全

解决方案 :

  • 首先就是 Collections工具类 Collections.synchronizedSet(new HashSet());
  • 就是和集合再同一个解决方案里的 Set set = new CopyOnWriteArraySet();
public class SetTest {
    //java.util.ConcurrentModificationException 同理和集合没有什么区别
    public static void main(String[] args) {
        /* 如何解决hashset 线程不安全?
         * 1. 首先就是 Collections工具类 Collections.synchronizedSet(new HashSet());
         * 2. 就是和集合再同一个解决方案里的  Set set = new CopyOnWriteArraySet();
         * */
        Set set = new CopyOnWriteArraySet();
        for (int i = 1; i <= 600; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

HashSet的底层 是 hashmap

public HashSet(){
    map = new HashMap<>();
}

//add set 的本质就是map key 是无法重复的
public boolean add(E e){
    return map.put(e,PRESENT)==null;
}

private static final Object PRESENT = new Object //不变的值
Map类不安全

HashMap原理简单回顾

image-20220302112806560

解决方案:

  • Map<String, String> map = new ConcurrentHashMap<>();
public class mapTest {
    //java.util.ConcurrentModificationException
    public static void main(String[] args) {
        //    map是这样用的吗 不是 工作中很少使用 Hashmap
        //    默认等价于什么 new HashMap<>(16,0.75);
        /*解决方案
         *
         * */
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 500; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }

    }
}
ConcurrentHashMap原理
        ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构, 一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素, 每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
ConcurrentHashMap的内部结构

image-20220309163044870

从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),所以,通过这一种结构,ConcurrentHashMap的并发能力可以大大的提高。

相关文章
|
IDE Linux 开发工具
从旧服务器迁移svn到另一台新服务器中(linux系统)|遇到诸多坑,已解决
从旧服务器迁移svn到另一台新服务器中(linux系统)|遇到诸多坑,已解决
|
XML 并行计算 算法
[Eigen中文文档] 求解稀疏线性系统
在Eigen中,有多种方法可用于求解稀疏系数矩阵的线性系统。由于此类矩阵的特殊表示,必须特别小心以获得良好的性能。本文列出了Eigen中可用的稀疏求解器。还介绍了所有这些线性求解器共同的主要步骤。根据矩阵的属性、所需的准确度,最终用户可以调整这些步骤以提高其代码的性能。请注意,并不需要深入了解这些步骤背后的内容:最后一节介绍了一个基础例程,可轻松使用以获取所有可用求解器的性能洞察。
706 0
|
6月前
|
机器学习/深度学习 C++
强化学习:实践理解Markov决策过程(MDP)(干中学系列)——手把手教你入门强化学习(三)
本博客以实践为主,带领读者巩固上期关于“Markov决策过程”的核心概念。通过构建学生马尔可夫奖励模型、计算收获值与状态价值,进一步验证贝尔曼方程。详细介绍了转移概率、奖励值及策略概率的设置,并实现了均匀随机策略下的状态价值计算与最优策略的价值评估。结合代码实例,帮助读者深入理解强化学习理论。适合初学者实践与进阶学习。
229 63
|
人工智能
【2024美赛】在COMAP比赛中使用大型语言模型和生成式AI工具的政策Use of Large Language ModelGenerative AI Tools in COMAP Contests
【2024美赛】在COMAP比赛中使用大型语言模型和生成式AI工具的政策Use of Large Language ModelGenerative AI Tools in COMAP Contests
280 1
|
供应链 监控 数据挖掘
ERP系统中的供应商协作与供应商评估解析
【7月更文挑战第25天】 ERP系统中的供应商协作与供应商评估解析
499 1
|
存储 数据格式 Python
如何使用 numpy 加载 txt 文件数据?
【8月更文挑战第30天】
551 0
|
XML JavaScript 前端开发
节点的属性
XML DOM 中的每个节点都是对象,具备方法和属性,可通过JavaScript处理。关键属性有:nodeName、nodeValue和nodeType。nodeType属性指示节点类型,是个只读属性。主要节点类型包括:元素(1)、属性(2)、文本(3)、注释(8)和文档(9)。
|
Java 测试技术 Spring
|
编解码 前端开发
css的基础知识
【4月更文挑战第11天】css的基础知识
115 1
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp微信小程序的体育资讯软件的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的体育资讯软件的详细设计和实现
101 0

热门文章

最新文章