Java多线程并发编程中并发容器第二篇之List的并发类讲解

概述

本文我们将详细讲解list对应的并发容器以及用代码来测试ArrayList、vector以及CopyOnWriteArrayList在100个线程向list中添加1000个数据后的比较

本文是《凯哥分享Java并发编程之J.U.C包讲解》系列教程中的第六篇。如果想系统学习,凯哥(kaigejava)建议从第一篇开始看。

从本篇开始,我们就来讲解讲解Java的并发容器。大致思路:先介绍什么是并发容器。然后讲解list相关的、map相关的以及队列相关的。这个系列会有好几篇文章。大家最好跟着一篇一篇学。

正文开始

并发容器分类讲解

CopyOneWriteArrayList

Copy-One-Write:即写入时候复制。

我们知道在原来List子类中vactor是同步容器线程安全的。这个CopyOneWriteArrayList可以理解为是他的并发替代品。

其底层数据结构也是数值。和ArrayList的不同之处就在于:在list对象中新增或者是删除元素的时候会把原来的集合copy一份,增删操作是在新的对象中操作的。操作完成之后,会将新的数组替换原来的数组。

我们来看看CopyOnWriteArrayList源码中的add方法:

编辑

我们来看看setArray方法:

编辑

发现了吗?变量使用的是transient和volatile两个关键之来修饰的。

在之前文章中,我们知道了volatile关键字是内存可见性。那么transient关键字是干嘛的呢?我们来看下百科解释:

编辑

关键的一句话:用transient关键字修饰的成员变量不用参与序列化过程。

添加注释后的源码:

编辑

public boolean add(E e) {

final ReentrantLock lock = this.lock;

//获取到锁

lock.lock();

try {

//获取到原集合

Object[] elements = getArray();

int len = elements.length;

//将原集合copy一份到新的集合中。并设置新的集合的长度为原集合长度+1

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

//将需要新增的元数添加到新的素组中

newElements[len] = e;

//将新数组替换原来数据。 使用transient和volatitle关键字修饰的

setArray(newElements);

return true;

} finally {

lock.unlock();

}

}

代码很简单,大致流程如下:

先从ReentrantLock中获取到锁(这样在多线程下可以防止其他线程来修改容器list里面内容了);

通过arrays.copyOf方法copy出一份原有数组长度+1;

将要添加的元素赋值给copy出来的数组;

使用setArray方法将新得数组替换原有素组。

因为都是List集合。我们就拿ArrayList、vector以及CopyOnWriteArrayList进行比较:

ArrayList、vector以及CopyOnWriteArrayList比较

业务场景描述:

启动100个线程,向对象中添加1000个数据。查看各自运行结果耗时及插入数据总数。代码在文章最后凯哥会贴出来。

先用线程非安全的arrayList执行效果:

编辑

执行arryList时间为 : 112毫秒!

List.size() : 93266

我们发现list的长度不对。正确的应该是100*1000.从结果来看,arrayList丢数据了。

使用Vector执行后的效果:

编辑

执行vector时间为 : 98毫秒!

List.size() : 100000

执行的总数对,说下同步锁没有丢数据。

在来看看copyOnWriteArrayList执行效果:

编辑

执行copyOnWriteArrayList时间为 : 5951毫秒!

List.size() : 100000

运行后数据比较:

运行的类

运行时长

运行结果

ArrayList

112毫秒

93266

Vector

98毫秒

100000

copyOnWriteArrayList

5951毫秒

100000

从上面表格中我们可以看出非安全线程的容器会丢数据。使用copyOneWriteArrayList耗时很长。那是因为每次运行都要copyof一份。

总结

copyArrayList(这里凯哥就简写了):是读写分离的。在写的时候会复制一个新的数组来完成插入和修改或者删除操作之后,再将新的数组给array.读取的时候直接读取最新的数据。

因为在写的时候需要向主内存申请控件,导致写操作的时候,效率非常低的(虽然在操作时候比较慢得,但是在删除或者修改数组的头和尾的时候还是很快的。因为其数据结构决定查找头和尾快,而且执行不需要同步锁)

从上面表中,可以看出copyArrayList虽然保证了线程的安全性,但是写操作效率太low了。但是相比Vector来说,在并发安全方面的性能要比vector好;

CopyArrayList和Vector相比改进的地方:

Vector是在新增、删除、修改以及查询的时候都使用了Synchronized关键字来保证同步的。但是每个方法在执行的时候,都需要获取到锁,在获取锁等待的过程中性能就会大大的降低的。

CopyArrayList的改进:只是在新增和删除的方法上使用了ReentrantLock锁进行(这里凯哥就不截图源码了,自己可以看看源码)。在读的时候不加锁的。所以在读的方面性能要不vector性能要好。

所以,CopyArrayList支持读多写少的并发情况

CopyOnWriteArrayList的使用场景:

由于读操作不加锁,增删改三个操作加锁的,因此适用于读多写少的场景,

局限性:因为读的时候不加锁的,读的效率和普通的arrayList是一样的。但是请看读操作:

编辑

编辑

在get的时候array使用的是volatile修饰的。是内存可见的。所以可以说copyArrayList在读的时候不会出现arrayList读取到脏数据的问题。

Get(i)方法比较如下:

联系凯哥:
个人博客:www.kaigejava.com
公众号:凯哥Java(kaigejava)

附件:arrayList、vector、copyOnwriteArrayList比较的代码:

public static void main(String[] args) {
//使用线程不安全的arrayList
// List<String> arryList = new ArrayList<>();
//使用vector
// List<String> arryList = new Vector<>();


//使用copyOnWriteArrayList
List<String> arryList = new CopyOnWriteArrayList<>();

Random random = new Random();
Thread [] threadArr = new Thread[100];
CountDownLatch latch = new CountDownLatch(threadArr.length);
Long beginTime = System.currentTimeMillis();
for(int i = 0;i<threadArr.length;i++){
threadArr[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 1000; j++){
try {
arryList.add("value" + random.nextInt(100000));
} catch (Exception e) {

}
}
latch.countDown();
}
});
}
for(Thread t : threadArr){
t.start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("执行copyOnWriteArrayList时间为 : " + (endTime-beginTime) + "毫秒!");
System.out.println("List.size() : " + arryList.size());
}

Java多线程并发编程中并发容器第二篇之List的并发类讲解的更多相关文章

  1. 【Java并发编程】6、volatile关键字解析&内存模型&并发编程中三概念

    volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...

  2. Java并发编程中的相关注解

    引自:http://www.cnblogs.com/phoebus0501/archive/2011/02/21/1960077.html Java并发编程中,用到了一些专门为并发编程准备的 Anno ...

  3. volatile关键字解析&内存模型&并发编程中三概念

    原文链接: http://www.cnblogs.com/dolphin0520/p/3920373.html volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java5之前,它是一个 ...

  4. Java的并发编程中的多线程问题到底是怎么回事儿?

    在我之前的一篇<再有人问你Java内存模型是什么,就把这篇文章发给他.>文章中,介绍了Java内存模型,通过这篇文章,大家应该都知道了Java内存模型的概念以及作用,这篇文章中谈到,在Ja ...

  5. Java多线程学习(七)并发编程中一些问题

    本节思维导图: 关注微信公众号:"Java面试通关手册" 回复"Java多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...

  6. Java并发编程中的设计模式解析(二)一个单例的七种写法

    Java单例模式是最常见的设计模式之一,广泛应用于各种框架.中间件和应用开发中.单例模式实现起来比较简单,基本是每个Java工程师都能信手拈来的,本文将结合多线程.类的加载等知识,系统地介绍一下单例模 ...

  7. Java并发编程中的若干核心技术,向高手进阶!

    来源:http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在 ...

  8. Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  9. 并发编程中.net与java的一些对比

    Java在并发编程中进行使用java.util.concurrent.atomic来处理一些轻量级变量 如AtomicInteger AtomicBoolean等 .Net中则使用Interlocke ...

  10. 【转】Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

随机推荐

  1. vba--数组,多个表中的程序合并到一起,设置为一个按钮

    Sub ttt() t = Timer Application.DisplayAlerts = False '清空数据 Sheets("买卖4").Select Range(&qu ...

  2. win10 VMware 关闭虚拟机失败导致再打开时显示连接不上虚拟机的一种解决方法

    VMware关闭虚拟机失败,强行关闭后,再次打开VMware,打开虚拟机时提示连接不上虚拟机,没有访问权限. 先试了退出后,用管理员权限打开,无果. 然后从网上查资料,cmd->services ...

  3. PHP str_replace() 函数详解

    PHP str_replace() 函数详解 1.前言: str_replace() 函数以其他字符替换字符串中的一些字符(区分大小写). 该函数区分大小写.请使用 str_ireplace() 函数 ...

  4. [BJDCTF2020]Mark loves cat(源码泄露+命令执行)

    扫描之后发现是/.git源码泄露 python GitHack.py http://56ad87c1-d8fb-463d-9480-f0fbee5176a0.node5.buuoj.cn:81/.gi ...

  5. [oeasy]python0105_七段数码管_7_SEGMENT_数码管驱动_4511

    七位数码管 回忆上次内容 上次回顾了 指示灯 辉光管   并了解了 驱动(driver) 驱动 就是 控制设备 工作的人(模块)     辉光管离我们的生活很远了 ​   添加图片注释,不超过 140 ...

  6. 题解:AT_arc173_b [ARC173B] Make Many Triangles

    背景 前几天打了比赛,崩麻了,所以来水一篇题解.LC真睿智 题意 给你 \(n\) 个点,问最多能组成几个三角形. 分析 听说可以随机化.这道题就是一个简单贪心. 我们考虑,如果没有共线的点,那么答案 ...

  7. CF1282A 题解

    洛谷链接&CF 链接 题目简述 共有 \(T\) 组数据. 有一条数轴,要从 \(a\) 点跑到 \(b\) 点,在 \(c\) 点的地方有网络,覆盖区间为 \([c - r, c+ r]\) ...

  8. CF709B 题解

    洛谷链接&CF 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 给定 \(N\) 个点,在一条数轴上,位置为 \(x_1,-,x_n\),你的位置为 \(p\) ...

  9. JavaScript 常用 Web APIs

    Web APIs Web APIs DOM 页面文档对象模型 DOM 树 获取元素 事件 监听事件 事件解绑 操作元素 样式属性操作 结点操作 结点关系 创建节点 DOM 事件流 事件对象 常用鼠标事 ...

  10. Activity的创建

    Activity的创建: 1.layout内写入相关代码 此处为显示的页面 2.Java内创建相关类写入代码 3.在清单内写入 快捷方法:直接完成上面步骤 layout: match_parent// ...