并发编程之的ArrayList安全性的详细解析

简介: 并发编程之的ArrayList安全性的详细解析

ArrayList不安全

ArrayList中的add方法没有synchronized修饰,是不安全的

下面代码运行结果(异常 java.util.ConcurrentModificationException):



import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
 * @author zkw
 * @Description list不安全
 */
public class ThreadList {
    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);
            }, i+"").start();
        }
    }
}
解决方法
  1. 使用Vector (已过时) 使用的是synchronized机制
  2. 使用Collections.synchronizedList()方法修饰ArrayList (性能不高)
    Collections中还有synchronizedMap和synchronizedSet的方法,可以修饰线程不安全的HashSet,HashMap
  3. 使用java.util.concurrent.CopyOnWriteArrayList(推荐)使用的是lock锁

使用CopyOnWriteArrayList

底层使用的是ReentrantLock可重用锁

下面给出CopyOnWriteArrayList 的 add源码

 

 final transient ReentrantLock lock = new ReentrantLock();    
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#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();
        }
    }

案例运行结果:

案例源码:



import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * @author zkw
 * @Description list不安全
 */
public class ThreadList {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            }, i+"").start();
        }
    }
}

CopyOnWriteArrayList解析

CopyOnWriteArrayList 利用的是读写分离的思想,读和写的是不同的容器

底层使用的是一个Object数组,每次新添加元素的时候利用的是Arrays.copyOf来创建一个新数组达到扩容效果

使用的是ReentrantLock来保证线程安全

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

 

 final transient ReentrantLock lock = new ReentrantLock();  
    private transient volatile Object[] array;
    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return {@code true} (as specified by {@link Collection#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); //将新容器设置为该list的容器
            return true;
        } finally {
            lock.unlock();
        }
    }


相关文章
|
存储 NoSQL 编译器
实战总结|抽丝剥茧,记一次神奇的崩溃
本文详细回放了一个崩溃案例的分析过程。回顾了C++多态和类内存布局、pc指针与芯片异常处理、内存屏障的相关知识。
|
人工智能 安全 搜索推荐
人脸识别在当今世界的重要性是什么?
鉴于全球范围内的 COVID-19 流行病,我们可能会期待对面部识别等生物识别技术进行更多投资。
人脸识别在当今世界的重要性是什么?
|
SQL druid 关系型数据库
Seata AT 分支事务
前面,我们已经介绍了 Seata 的整体设计思想,接下来我们深入到其实现细节中,本文介绍 Seata 中 AT 模式分支事务的实现。
|
Oracle 关系型数据库 Linux
Linux系统中Oracle数据库使用SELECT语句检索数据(1)实例应用
1,首先切换到Oracle用户,并进入数据库#sql / as sysdba2,启动数据库,并连接样例及表格,启动命令#startup,连接样例#conn scott/tiger3,select语句中:不区分大小写;可以写一行或多行,为方便查看最好每个子句单独一行;语句以“;”结尾结束语句4,se.
|
开发工具 C++
DirectX9.0 SDK 安装过程
感谢下面的参考博客 http://www.cnblogs.com/xiaojinma/archive/2012/12/07/2806635.html   一、为什么要用DirectX9.0 SDK      Microsoft DirectX 是这样一组技术:它们旨在使基于Windows 的计算机成为运行和显示具有丰富多元素(例如全色图形、视频、3D 动画和丰富音频)的应用程序的理想平台。
1845 0
|
监控 Oracle 关系型数据库
ORACLE备份策略(ORACLE BACKUP STRATEGY)
概要 1、了解什么是备份 2、了解备份的重要性 3、理解数据库的两种运行方式 4、理解不同的备份方式及其区别 5、了解正确的备份策略及其好处   一、了解备份的重要性 可以说,从计算机系统出世的那天起,就有了备份这个概念,计算机以其强大的速度处理能力,取代了很多人为的工作,但是,往往很多时候,它又是那么弱不禁风,主板上的芯片、主板电路、内存、电源等任何一项不能正常工作,都会导致计算机系统不能正常工作。
1328 0
|
5天前
|
数据采集 人工智能 自然语言处理
3分钟采集134篇AI文章!深度解析如何通过云无影AgentBay实现25倍并发 + LlamaIndex智能推荐
结合阿里云无影 AgentBay 云端并发采集与 LlamaIndex 智能分析,3分钟高效抓取134篇 AI Agent 文章,实现 AI 推荐、智能问答与知识沉淀,打造从数据获取到价值提炼的完整闭环。
390 93
|
6天前
|
域名解析 人工智能
【实操攻略】手把手教学,免费领取.CN域名
即日起至2025年12月31日,购买万小智AI建站或云·企业官网,每单可免费领1个.CN域名首年!跟我了解领取攻略吧~