1.ArrayList源码和多线程安全问题分析

在分析ArrayList线程安全问题之前,我们线对此类的源码进行分析,找出可能出现线程安全问题的地方,然后代码进行验证和分析。

1.1 数据结构

ArrayList内部是使用数组保存元素的,数据定义如下:

transient Object[] elementData; // non-private to simplify nested class access

在ArrayList中此数组即是共享资源,当多线程对此数据进行操作的时候如果不进行同步控制,即有可能会出现线程安全问题。

1.2 add方法可能出现的问题分析

首先我们看一下add的源码如下:

public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}

此方法中有两个操作,一个是数组容量检查,另外就是将元素放入数据中。我们先看第二个简单的开始分析,当多个线程执行顺序如下所示的时候,会出现最终数据元素个数小于期望值。

按照此顺序执行完之后,我们可以看到,elementData[n]的只被设置了两次,第二个线程设置的值将前一个覆盖,最后size=n+1。下面使用代码进行验证此问题。

1.3 代码验证

首先先看下以下代码,开启1000个线程,同时调用ArrayList的add方法,每个线程向ArrayList中添加100个数字,如果程序正常执行的情况下应该是输出:

list size is :10000

代码如下:

private static List<Integer> list = new ArrayList<Integer>();

    private static ExecutorService executorService = Executors.newFixedThreadPool(1000);

    private static class IncreaseTask extends Thread{
@Override
public void run() {
System.out.println("ThreadId:" + Thread.currentThread().getId() + " start!");
for(int i =0; i < 100; i++){
list.add(i);
}
System.out.println("ThreadId:" + Thread.currentThread().getId() + " finished!");
}
} public static void main(String[] args){
for(int i=0; i < 1000; i++){
executorService.submit(new IncreaseTask());
}
executorService.shutdown();
while (!executorService.isTerminated()){
try {
Thread.sleep(1000*10);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("All task finished!");
System.out.println("list size is :" + list.size());
}

当执行此main方法后,输出如下:

从以上执行结果来看,最后输出的结果会小于我们的期望值。即当多线程调用add方法的时候会出现元素覆盖的问题。

1.4 数组容量检测的并发问题

在add方法源码中,我们看到在每次添加元素之前都会有一次数组容量的检测,add中调用此方法的源码如下:

ensureCapacityInternal(size + 1);

容量检测的相关源码如下:

private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}

容量检测的流程图如下所示:

我们以两个线程执行add操作来分析扩充容量可能会出现的并发问题:

当我们新建一个ArrayList时候,此时内部数组容器的容量为默认容量10,当我们用两个线程同时添加第10个元素的时候,如果出现以下执行顺序,可能会抛出java.lang.ArrayIndexOutOfBoundsException异常。

第二个线程往数组中添加数据的时候由于数组容量为10,而此操作往index为10的位置设置元素值,因此会抛出数组越界异常。

1.5 代码验证数组容量检测的并发问题

使用如下代码:

private static List list = new ArrayList(3);

    private static ExecutorService executorService = Executors.newFixedThreadPool(10000);

    private static class IncreaseTask extends Thread{
@Override
public void run() {
System.out.println("ThreadId:" + Thread.currentThread().getId() + " start!");
for(int i =0; i < 1000000; i++){
list.add(i);
}
System.out.println("ThreadId:" + Thread.currentThread().getId() + " finished!");
}
} public static void main(String[] args){ new IncreaseTask().start();
new IncreaseTask().start();
}

执行main方法后,我们可以看到控制台输出如下:

1.6 ArrayList中其他方法说明

ArrayList中其他包含对共享变量操作的方法同样会有并发安全问题,只需要按照以上的分析方法分析即可。

ArrayList源码和多线程安全问题分析的更多相关文章

  1. Java集合干货——ArrayList源码分析

    ArrayList源码分析 前言 在之前的文章中我们提到过ArrayList,ArrayList可以说是每一个学java的人使用最多最熟练的集合了,但是知其然不知其所以然.关于ArrayList的具体 ...

  2. Java集合框架之一:ArrayList源码分析

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! ArrayList底层维护的是一个动态数组,每个ArrayList实例都有一个容量.该容量是指用来存储列表元素的数组的大小.它总是至少等于 ...

  3. Java基础 ArrayList源码分析 JDK1.8

    一.概述 本篇文章记录通过阅读JDK1.8 ArrayList源码,结合自身理解分析其实现原理. ArrayList容器类的使用频率十分频繁,它具有以下特性: 其本质是一个数组,因此它是有序集合 通过 ...

  4. ArrayList 源码分析

    ArrayList 源码分析 1. 结构   首先我们需要对 ArrayList 有一个大致的了解就从结构来看看吧. 1. 继承   该类继承自 AbstractList 这个比较好说 2. 实现 这 ...

  5. ArrayList源码分析超详细

    ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要分析的类(ztrl+N查找ArraLi ...

  6. Java -- 基于JDK1.8的ArrayList源码分析

    1,前言 很久没有写博客了,很想念大家,18年都快过完了,才开始写第一篇,争取后面每周写点,权当是记录,因为最近在看JDK的Collection,而且ArrayList源码这一块也经常被面试官问道,所 ...

  7. Java - ArrayList源码分析

    java提高篇(二一)-----ArrayList 一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 nul ...

  8. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  9. ArrayList源码分析超详细(转载)

    ArrayList源码分析超详细   ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...

随机推荐

  1. C#使用Gecko实现浏览器

    Gecko就是火狐浏览器的内核啦,速度很快,兼容性比.net内置的webbrowser高到不知哪里去了. 使用Gecko首先要下载一堆依赖库,主要是Skybound.Gecko和xulrunner. ...

  2. jquery中substring,substr,split的用法

    一.substring 方法 返回位于 String 对象中指定位置的子字符串. strVariable.substring(start, end) "String Literal" ...

  3. fedora27安装DB2 Express-C 11

    首先在官网下载对应的安装包和语言包两个文件. 然后通过tar -zxvf命令将下载的两个文件解压. 其中一个文件解压后是名为expc的文件,进入这个文件.里面有一个名为db2setup的文件. 在命令 ...

  4. Spring-cloud(二)注册服务提供者搭建

    上文已经写了如何去搭建注册中心,仅有注册中心是远远不够的,所以我们需要注册到注册中心并提供服务的节点,这里称为注册服务提供者 前提 阅读上文,并成功搭建注册中心,环境无需改变 项目搭建 这里我们需要新 ...

  5. ionic新入坑-环境搭建+新建项目+打开低版本项目处理

    是的.我又双叒叕入新坑了.想我大学的时候web-app刚火起来.还帮忙做了我们学校医务室系统的web-app页面部分呢.时间太紧最后也没出个完整的版本.那时候只是用H5简单做了web部分.是想着用ph ...

  6. 去除Eclipse中js报错的问题

    第一步:    去除eclipse的JS验证:        将windows->preference->Java Script->Validator->Errors/Warn ...

  7. Angular CLI 安装

    安装Angular 官网的教程,因为国内网络环境原因,访问不了服务器,导致安装失败. 1.先安装NodeJs 安装教程:http://blog.csdn.net/zengmingen/article/ ...

  8. opencv2.4.9卸载安装

    1.安装opencv2.4.9过程中遇到的问题. 1.使用 sudo apt-get install libopencv-dev 安装OpenCV 2.4.9 发现版本为2.4.8,故卸载重装. 查看 ...

  9. [LeetCode] Maximum Average Subarray I 子数组的最大平均值

    Given an array consisting of n integers, find the contiguous subarray of given length k that has the ...

  10. centOS7配置DNS服务器

    世上无难事只怕有心人,遇事千万千万不要抵触,消极的情绪是失败之母,一点一滴,踏踏实实是通往幸福的捷径. 历经激动,受挫,抵触和鼓励以及征服,终于配好了让我欢喜让我忧的dns.在这里记录下来,希望能够给 ...