为什么会有CopyOnWriteArrayList?


我们知道ArrayList和LinkedList实现的List都是非线程安全的,于是就有了Vector,它是基于ArrayList的线程安全集合,但Vector无论是add方法还是get方法都加上了synchronized修饰,当多线程读写List必须排队执行,很显然这样效率比较是低下的,那有没有一种办法让效率提升,让当读List的时候线程是异步的,当写List是同步的呢?答案是CopyOnWriteArrayList,他是读写分离的,好处是提高线程访问效率,下面我们对比下CopyOnWriteArrayList和Vector执行效率。

import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; /**
* @author :jiaolian
* @date :Created in 2021-01-18 15:28
* @description:安全list性能对比
* @modified By:
* 公众号:叫练
*/
public class SafeListTest { private static Vector<String> safeList = new Vector<>();
//private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>(); private static CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException {
//初始化
safeList.add("叫练");
MySerive fishSerive = new MySerive();
long start = System.currentTimeMillis();
new Thread(()->{
fishSerive.read();
countDownLatch.countDown();
},"叫练读线程").start();
new Thread(()->{
fishSerive.write();
countDownLatch.countDown();
},"叫练写线程").start();
countDownLatch.await();
System.out.println("花费:"+(System.currentTimeMillis()-start));
} private static class MySerive {
//读
public void read() {
for (int i=0 ;i<1000000; i++) {
safeList.get(0);
}
} //写
public void write() {
for (int i=0 ;i<100000; i++) {
safeList.add("叫练");
}
}
}
}

如上代码:当安全集合用Vector时,执行时长是100毫秒,当安全集合用CopyOnWriteArrayList时,执行时长是5000毫秒,神码?你不是说CopyOnWriteArrayList的效率要高么?但执行情况CopyOnWriteArrayList执行的时长竟然是Vector的50倍!通过翻看源码,我们发现当CopyOnWriteArrayList写元素时是通过备份数组的方式实现的,当多线程同步激烈,数据量较大时会不停的复制数组,内存浪费严重。这就是时过长的原因!但是我们还是认可读写分离思想!

什么是弱一致性


import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList; /**
* @author :jiaolian
* @date :Created in 2021-01-18 16:40
* @description:CopyOnWriteArrayList弱一致性
* @modified By:
* 公众号:叫练
*/
public class WeekCopyOnWriteArrayListTest { private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();
//private static Vector<String> safeList = new Vector<>(); public static void main(String[] args) throws InterruptedException {
safeList.add("叫");
safeList.add("练");
Iterator<String> iterator = safeList.iterator();
Thread thread = new Thread(()->{
//删除下标为0的元素
safeList.remove(0);
});
thread.start();
//主线程等待thread执行完成;
thread.join();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
} }

如上代码:主线程等待thread子线程执行完毕,循环打印safeList元素,最终执行结果如下图所示

你可能会有疑问,thread不是已经删除“叫”吗?控制台不是应该只打印一个“练”字吗?为什么还会打出“叫练”两个字,原因是main线程在执行Iterator<String> iterator = safeList.iterator();保存了元素快照,所以能看到这样的执行结果,当thread线程执行完毕后,此时JVM内存状态如下图所示!

fail-safe特性


提到fail-safe,会先提到fail-fast,字面上翻译快速失败,它是集合快速检测失败机制,防止集合不正确操作!一般情况下,如果线程通过iterator方式循环集合时,另外一个线程也修改了这个集合,我们测试下,如上述测试弱一致性的代码,将private static CopyOnWriteArrayList<String> safeList = new CopyOnWriteArrayList<>();换成private static Vector<String> safeList = new Vector<>();会发生什么情况呢?

如上图,java.util.ConcurrentModificationException,集合并发修改错误,但换成CopyOnWriteArrayList执行正常,原因是CopyOnWriteArrayList删除数据时会有集合快照。

所以他是fail-safe,而Vector是fail-fast!

总结


总结下吧,我们用代码简述说明了CopyOnWriteArrayList的读写分离,弱一致性,fail-safe,fail-safe等概念,并简述了实现原理。喜欢的请点赞加关注哦。我是叫练【公众号】,边叫边练。

CopyOnWriteArrayList 读写分离,弱一致性的更多相关文章

  1. Java进阶知识点6:并发容器背后的设计理念 - 锁分段、写时复制和弱一致性

    一.背景 容器是Java编程中使用频率很高的组件,但Java默认提供的基本容器(ArrayList,HashMap等)均不是线程安全的.当容器和多线程并发编程相遇时,程序员又该何去何从呢? 通常有两种 ...

  2. Mycat读写分离、主从切换、分库分表的操作记录

    系统开发中,数据库是非常重要的一个点.除了程序的本身的优化,如:SQL语句优化.代码优化,数据库的处理本身优化也是非常重要的.主从.热备.分表分库等都是系统发展迟早会遇到的技术问题问题.Mycat是一 ...

  3. Oceanbase读写分离方案探索与优化

    [作者] 许金柱,携程资深DBA,专注于分布式数据库研究及运维. 台枫,携程高级DBA,主要负责MySQL和OceanBase的运维. [前言]    读写分离,是一种将数据库的查询操作和写入操作分离 ...

  4. MySQL+Amoeba实现数据库主从复制和读写分离

    MySQL读写分离是在主从复制的基础上进一步通过在master上执行写操作,在slave上执行读操作来实现的.通过主从复制,master上的数据改动能够同步到slave上,从而保持了数据的一致性.实现 ...

  5. CYQ.Data V5 数据库读写分离功能介绍

    前言 好多年没写关于此框架的新功能的介绍了,这些年一直在默默地更新,从Nuget上的记录就可以看出来: 这几天在看Java的一些东西,除了觉的Java和.NET的相似度实在太高之外,就是Java太原始 ...

  6. EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~终结~配置的优化和事务里读写的统一

    回到目录 本讲是通过DbCommand拦截器来实现读写分离的最后一讲,对之前几篇文章做了一个优化,无论是程序可读性还是实用性上都有一个提升,在配置信息这块,去除了字符串方式的拼接,取而代之的是sect ...

  7. LVS+MYCAT读写分离+MYSQL同步部署手册(第三版)

    1      配置MYSQL主备同步 1.1    测试环境 mysql版本:5.6.24: 操作系统内核版本:Linux-3.13-0-32 主数据库IP:192.168.10.3: 主数据库名:d ...

  8. LVS+MYCAT+读写分离+MYSQL主备同步部署手册

    LVS+MYCAT+读写分离+MYSQL主备同步部署手册 1          配置MYSQL主备同步…. 2 1.1       测试环境… 2 1.2       配置主数据库… 2 1.2.1  ...

  9. MySQL高可用读写分离方案预研

    目前公司有需求做MySQL高可用读写分离,网上搜集了不少方案,都不尽人意,下面是我结合现有组件拼凑的实现方案,亲测已满足要求,希望各位多提建议 :) 一.    网上方案整理(搜集地址不详...) 1 ...

随机推荐

  1. Servlet中获取请求参数问题

    1.GET方法,可以通过getParamter方法反复获取同一个变量的数据: 2.POST方法,需要注意请求类型(content-Type)是否是application/x-www-form-urle ...

  2. spark streaming 消费 kafka入门采坑解决过程

    kafka 服务相关的命令 # 开启kafka的服务器bin/kafka-server-start.sh -daemon config/server.properties &# 创建topic ...

  3. MySQL锁(三)行锁:幻读是什么?如何解决幻读?

    概述 前面两篇文章介绍了MySQL的全局锁和表级锁,今天就介绍一下MySQL的行锁. MySQL的行锁是各个引擎内部实现的,不是所有的引擎支持行锁,例如MyISAM就不支持行锁. 不支持行锁就意味着在 ...

  4. css进阶 03-网页设计和开发中,关于字体的常识

    03-网页设计和开发中,关于字体的常识 #前言 我周围的码农当中,有很多是技术大神,却常常被字体这种简单的东西所困扰. 这篇文章,我们来讲一讲关于字体的常识.这些常识所涉及到的问题,有很强的可操作性, ...

  5. Python高级语法-贯彻回顾-元类(4.99.1)

    @ 目录 1.为什么要掌握元类 2.正文 关于作者 1.为什么要掌握元类 在django中编写models的时候遇到了元类的相关操作 并且在mini-web框架编写的时候也遇到了相关的问题 意识到深入 ...

  6. js 获取某月第一天和最后一天

    1.获取某月第一天和最后一天日期 function getDateByMonth (timeStamp) { let inDate = new Date(timeStamp) let year = i ...

  7. Backdrop Filter

    CSS 滤镜 : backdrop-filter backdrop filter属性允许我们使用css对元素后面的内容应用过滤效果. 滤镜: 名称: 方法案例: 效果: blur() 模糊 filte ...

  8. Cannot assign requested address 和 SO_REUSEADDR 参数

    1. 错误 今天项目中出现了大量的java.net.ConnectException: Cannot assign requested address (connect failed) 错误. 刚开始 ...

  9. PP主数据-物料主数据

    一.PP物料主数据:PP的物料主数据,是对应到系统的组织架构的,不同的组织层次,都有各自的主数据需要创建. (1),一般数据:一般数据是在集团层面的主数据,主要包括:物料编码.物料描述.辅助计量单位以 ...

  10. 码农英语四级考了6次,也能进知名IT外企

    程序员学英语 这显然不是新鲜的话题,但再怎么重复强调都不过分! 为啥要学 IT是当今世界发展最快的行业,没有之一!作为其中的从业人员,要始终保持对最新技术的关注度,难免需要阅读英文新闻或文章 平时工作 ...