前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JUC系列(三) 不安全的集合类

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

作者头像
冷环渊
发布2022-11-12 14:16:18
2480
发布2022-11-12 14:16:18
举报
文章被收录于专栏:冷环渊的全栈工程师历程

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

集合类不安全

List不安全

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

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

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

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

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

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

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

代码语言:javascript
复制
//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方法的原理

代码语言:javascript
复制
    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();
代码语言:javascript
复制
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

代码语言:javascript
复制
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
image-20220302112806560

解决方案:

  • Map<String, String> map = new ConcurrentHashMap<>();
代码语言:javascript
复制
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原理

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

ConcurrentHashMap的内部结构

image-20220309163044870
image-20220309163044870

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 集合类不安全
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档