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. manage.py“Couldn't import Django”报错的问题解决

    问题分析: 在pyharm中项目可以正常运行但是在终端 终端输入python manage.py runserver首次测试项目时,出现了无法引用Django的错误. Traceback (most ...

  2. 1.Javascript 快速入门(主要)

    运算 &&运算是与运算,只有所有都为true,&&运算结果才是true: true && true; // 这个&&语句计算结果为tru ...

  3. yb课堂 ECMAScript 6常见语法快速入门 《三十一》

    什么是ES 6 ECMAScript 6(简称ES6)是于2015年6月正式发布的JavaScript语言的标准,正式名为ECMAScript 2015(ES2015).它的目标是使用JavaScri ...

  4. PHP 程序员是学 Swoole ?还是学 Go ?

    大家好,我是码农先森. 面临现状 这次为什么要讨论这个话题,因为 Swoole 和 Go 在 PHP 程序员坊间一直都是茶语饭后的谈资,觉得懂 Swoole 和 Go 的就高人一等.相信有很多的 PH ...

  5. 基于 Impala 的高性能数仓建设实践之虚拟数仓

    导读:本文主要介绍网易数帆 NDH 在 Impala 上实现的虚拟数仓特性,包括资源分组.水平扩展.混合分组和分时复用等功能,可以灵活配置集群资源.均衡节点负载.提高查询并发,并充分利用节点资源. 接 ...

  6. [oeasy]python0094_视频游戏_双人网球_pong_atari_mos_6502_雅达利_米洛华

    编码进化 回忆上次内容 上次 我们回顾了 微软之前的 比尔盖茨和保罗艾伦 mits 迎来的 是帮手 还是隐患? intel-8080 遇到了 mos-6502 底层硬件 驱动 游戏行业进化 不光是扑克 ...

  7. [oeasy]教您玩转python - 0002 - 你好世界(hello world!)

    ​ 你好世界 回忆上次内容 这次我们, 了解了 Python 进入了 Python 退出了 Python 这并不难 这就是我们对于 Python 的初体验 恭喜您存活了下来! ​ 编辑 python ...

  8. Less预处理器的使用

    练习页面: <body> <div class="div1"> <div class="div2"></div> ...

  9. SEO初学指南之关键词研究(2) - 搜索意图分析

    Hi,我是听风.这篇文章我们深入探讨下关键词研究中最重要的一点:搜索意图. 虽然这在本系列教程的第一篇中提到过搜索意图,但是我还是想再写一篇文章来帮助你们了解什么是搜索意图以及如何再关键词研究中使用它 ...

  10. 安全可信,Solon v2.8.6 发布

    Solon 框架! Java "纯血国产"应用开发框架.开放原子开源基金会,孵化项目.从零开始构建(非 java-ee 架构),有灵活的接口规范与开放生态. 追求: 更快.更小.更 ...