java arraylist线程安全_ArrayList源码和多线程安全问题详解

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

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;

}

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

f8652d050d85b91ced8950a6dbda888b.png

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

1.3 代码验证

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

list size is :10000

代码如下:

private static List list = new ArrayList();

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方法后,输出如下:

6ffe66bcff5fe1034330b162fbdf8d2e.png

从以上执行结果来看,最后输出的结果会小于我们的期望值。即当多线程调用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);

}

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

ebe369e8abd820a81d53eefb3e894e2b.png

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

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

7b46aee4139a0f6917487bc8e2dca68f.png

第二个线程往数组中添加数据的时候由于数组容量为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方法后,我们可以看到控制台输出如下:

42abd697dc7e0ff88fa91a3cd0a2c0d2.png

1.6 ArrayList中其他方法说明

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

java实现ArrayList根据存储对象排序功能示例

本文实例讲述了java实现ArrayList根据存储对象排序功能。分享给大家供大家参考,具体如下:

与c++中的qsort的实现极为相似,构建新的比较对象Comparator即可

package demo;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

class Stu{

public int age;

private String name;

public Stu(String name,int age){

this.age=age;

this.name=name;

}

public String toString(){

String str=name+"is "+age;

return str;

}

}

public class Sort{

@SuppressWarnings({ "unchecked", "rawtypes" })

public static void main(String[] args){

ArrayList s=new ArrayList();

s.add(new Stu("cjc",25));

s.add(new Stu("jake",17));

s.add(new Stu("john",30));

s.add(new Stu("Alice",23));

System.out.println("码农之家测试结果:");

System.out.println("The order before sorting!");

System.out.println(s);

//对集合中的元素更具年龄进行排序

Collections.sort(s,new Comparator(){

@Override

public int compare(Object o1, Object o2) {

// TODO Auto-generated method stub

Stu stu1=(Stu)o1;

Stu stu2=(Stu)o2;

return stu2.age-stu1.age;

}

});

System.out.println("The order after sorting!");

System.out.println(s);

}

}

运行结果:

f6e52819eeb125b2336c9298142eabdc.png

PS:这里再为大家推荐一款关于排序的演示工具供大家参考:

在线动画演示插入/选择/冒泡/归并/希尔/快速排序算法过程工具:http://tools.jb51.net/aideddesign/paixu_ys

更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。

以上就是本次给大家分享的关于java的全部知识点内容总结,大家还可以在下方相关文章里找到相关文章进一步学习,感谢大家的阅读和支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值