JUC面试(五)——Collection线程不安全

这篇具有很好参考价值的文章主要介绍了JUC面试(五)——Collection线程不安全。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Collection线程不安全

前言

当我们执行下面语句的时候,底层进行了什么操作

new ArrayList<Integer>();

底层创建了一个空的数组,伴随着初始值为10

当执行add方法后,如果超过了10,那么会进行扩容,扩容的大小为原值的一半,也就是5个,使用下列方法扩容

Arrays.copyOf(elementData, netCapacity)

ArrayList线程不安全

单线程环境

单线程环境的ArrayList是不会有问题的

public class ArrayListNotSafeDemo {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        for(String element : list) {
            System.out.println(element);
        }
    }
}

多线程环境

为什么ArrayList是线程不安全的?因为在进行写操作的时候,方法上为了保证并发性,是没有添加synchronized修饰,所以并发写的时候,就会出现问题

JUC面试(五)——Collection线程不安全

当我们同时启动30个线程去操作List的时候

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 集合类线程不安全举例
 * @author: wzq
 * @create: 2020-03-12-20:15
 */
public class ArrayListNotSafeDemo {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

这个时候出现了错误,也就是java.util.ConcurrentModificationException

JUC面试(五)——Collection线程不安全

这个异常是 并发修改的异常

解决方案

方案一:Vector

第一种方法,就是不用ArrayList这种不安全的List实现类,而采用Vector,线程安全的

关于Vector如何实现线程安全的,而是在方法上加了锁,即synchronized

JUC面试(五)——Collection线程不安全

这样就每次只能够一个线程进行操作,所以不会出现线程不安全的问题,但是因为加锁了,导致并发性极度下降

方案二:Collections.synchronizedList()
List<String> list = Collections.synchronizedList(new ArrayList<>());

采用Collections集合工具类,在ArrayList外面包装一层 同步 机制,加同步锁,导致并发性极度下降。

前2个方案的区别:

同步代码块

synchronized(obj){   
    //需要被同步的代码块
}

其中,obj 称为同步监视器,也就是锁,原理是:当线程开始执行同步代码块前,必须先获得对同步代码块的锁定。并且任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。

  • Vector使用同步方法实现,synchronizedList使用同步代码块实现

    同步代码块和同步方法的区别 1.同步代码块在锁定的范围上可能比同步方法要小,一般来说锁的范围大小和性能是成反比的。 2.同步块可以更加精确的控制锁的作用域(锁的作用域就是从锁被获取到其被释放的时间),同步方法的锁的作用域就是整个方法。 3.同步代码块可以选择对哪个对象加锁,但是同步方法只能给this对象加锁。

  • SynchronizedList有很好的扩展和兼容功能。它可以将所有的List的子类转成线程安全的类

  • 使用SynchronizedList的时候,进行遍历时要手动进行同步处理

  • SynchronizedList可以指定锁定的对象

方案三:采用JUC里面的方法

CopyOnWriteArrayList:写时复制,主要是一种读写分离的思想

List<String> list = new CopyOnWriteArrayList<>();

写时复制,CopyOnWrite容器即写时复制的容器,往一个容器中添加元素的时候,不直接往当前容器Object[]添加,而是先将Object[]进行copy,复制出一个新的容器object[] newElements,然后新的容器Object[] newElements里添加原始,添加元素完后,在将原容器的引用指向新的容器 setArray(newElements);这样做的好处是可以对copyOnWrite容器进行并发的度,而不需要加锁,因为当前容器不需要添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器

就是写的时候,把ArrayList扩容一个出来,然后把值填写上去,在通知其他的线程,ArrayList的引用指向扩容后的新List。

CopyOnWriteArrayList虽然是一个线程安全版的ArrayList,但其每次修改数据时都会复制一份数据出来,所以只适用读多写少或无锁读场景。

高并发写时,CopyOnWriteArrayList为何这么慢呢?因为其每次add时,都用Arrays.copyOf创建新数组,频繁add时内存申请释放性能消耗大。

查看底层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();
        }
    }

首先需要加锁

final ReentrantLock lock = this.lock;
lock.lock();

然后在末尾扩容一个单位

Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);

然后在把扩容后的空间,填写上需要add的内容

newElements[len] = e;

最后把内容set到Array中

适合读多写少的情况

HashSet线程不安全

CopyOnWriteArraySet

底层还是使用CopyOnWriteArrayList进行实例化

JUC面试(五)——Collection线程不安全

HashSet底层结构

同理HashSet的底层结构就是HashMap

JUC面试(五)——Collection线程不安全

但是为什么我调用 HashSet.add()的方法,只需要传递一个元素,而HashMap是需要传递key-value键值对?

首先我们查看hashSet的add方法

   public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

我们能发现但我们调用add的时候,存储一个值进入map中,只是作为key进行存储,而value存储的是一个Object类型的常量,也就是说HashSet只关心key,而不关心value

HashMap线程不安全

HashMap底层,默认大小16(必须是2^n),加载因子默认0.75,0.75*容量=12,所以map容量达到13则扩容,大小可自定义。java8数据结构:数组+单向链表–>>节点>8红黑树。key相同,存进去会覆盖原value,但是其底层是链表,>8是红黑树。

同理HashMap在多线程环境下,也是不安全的

    public static void main(String[] args) {

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

        for (int i = 0; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

JUC面试(五)——Collection线程不安全

解决方法

1、使用Collections.synchronizedMap(new HashMap<>());

2、使用 ConcurrentHashMap文章来源地址https://www.toymoban.com/news/detail-422079.html

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

到了这里,关于JUC面试(五)——Collection线程不安全的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 信息安全面试题合集

    本篇会记录一些可能会遇到的面试题,持续更新 sql注入常见的闭合方式有哪些? Mysql5.0上下sql注入有什么区别? SQL注入空格被过滤,有什么绕过方式? 过滤了逗号,有什么绕过方式? md5 sql注入的原理? with rollup 绕过登录的原理? 什么是无列名注入,原理是什么? 如果i

    2024年02月11日
    浏览(45)
  • 移动安全面试题—抓包

    justTrustMe 的原理? justTrustMe 是一个 Xposed 模块,用于绕过 Android 应用的 SSL 证书验证。它的原理是 Hook 系统的 javax.net.ssl.X509TrustManager 类,使其在检查服务器证书时始终返回成功。这样,即使服务器使用了自签名证书或抓包工具的证书,应用也会接受。 了解过 SSL-pinning?SSL-

    2024年02月07日
    浏览(31)
  • 移动安全面试题—风控

    延迟处罚型风控如何对抗?群控(工作室)有哪些检测方式? 延迟处罚型风控是指在一段时间内收集和分析用户行为数据,然后根据分析结果对可疑行为进行处罚的风控策略。对抗延迟处罚型风控的方法包括: 行为建模: 对正常用户的行为进行建模,使得恶意行为更接近正

    2024年02月09日
    浏览(33)
  • 网络安全书籍推荐+网络安全面试题合集

    一、计算机基础 《深入理解计算机系统》 《鸟哥的Linux私房菜》 《TCP/IP详解(卷1:协议)》 《HTTP权威指南》 《Wireshark数据包分析实战》 《Wireshark网络分析的艺术》 《Wireshark网络分析就这么简单》 二、网络渗透 《白帽子讲Web安全》 《Web安全深度剖析》 《SQL注入天书》

    2023年04月25日
    浏览(35)
  • 2023秋招,网络安全面试题

     Hello,各位小伙伴,我作为一名网络安全工程师曾经在秋招中斩获🔟+个offer🌼,并在国内知名互联网公司任职过的职场老油条,希望可以将我的面试的网络安全大厂面试题和好运分享给大家~ 转眼2023年秋招已经到了金银🔟的关键阶段,宝子们简历抓紧准备投递起来呀,冲冲

    2024年02月15日
    浏览(36)
  • 移动安全面试题—调试&反调试

    Android反调试的几种手段 检测 TracerPid:在 /proc/self/status 文件中,TracerPid 字段表示调试进程的 PID。如果该值非零,则意味着当前进程被调试。 对抗方法:使用内核模块或 Xposed 插件拦截对 /proc/self/status 的读取,将 TracerPid 字段设置为 0。 检测调试端口:/proc/self/maps 文件中包含

    2024年01月19日
    浏览(29)
  • 网络安全面试题汇总(附答案)

    作为从业多年的网络安全工程师,我深知在面试过程中面试官所关注的重点及考察的技能点。网络安全作为当前信息技术领域中非常重要的一部分,对于每一个从事网络安全工作的人员来说,不仅需要掌握一定的技术能力,更需要具备全面的综合素质。 在我职业发展的过程中

    2024年02月13日
    浏览(41)
  • 2023网络安全面试题(附答案)+面经

    随着国家政策的扶持,网络安全行业也越来越为大众所熟知,相应的想要进入到网络安全行业的人也越来越多,为了拿到心仪的Offer之外,除了学好网络安全知识以外,还要应对好企业的面试。 所以在这里我归纳总结了一些网络安全方面的常见面试题,希望能对大家有所帮助

    2023年04月09日
    浏览(36)
  • 史上最全网络安全面试题+答案

    1、什么是SQL注入攻击 前端代码未被解析被代入到数据库导致数据库报错 2、什么是XSS攻击 跨站脚本攻击 在网页中嵌入客户端恶意脚本,常用s语言,也会用其他脚本语言 属于客户端攻击,受害者是用户,网站管理员也属于用户,攻击者一般也是靠管理员身份作为跳板 3、什么

    2024年02月13日
    浏览(34)
  • 史上最全网络安全面试题汇总

    最近有不少小伙伴跑来咨询: 想找网络安全工作,应该要怎么进行技术面试准备? 工作不到 2 年,想跳槽看下机会,有没有相关的面试题呢? 为了更好地帮助大家高薪就业,今天就给大家分享一份网络安全工程师面试题,希望它们能够帮助大家在面试中,少走一些弯路、更

    2024年02月07日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包