【JDK】:Java容器框架——同步容器与并发容器

2023-11-02

前面的文章中详细介绍了Java的容器框架,在此基础上,本文对Java中的同步容器与并发容器做一些介绍。

fail-fast机制

快速报错机制(fail-fast)能够防止多个进程同时修改同一个容器的内容。如果在你迭代遍历某个容器的过程中,另一个进程接入其中,并且插入、删除或者修改此容器内的某个对象,就会出现问题:也许迭代过程已经处理过容器中的该元素了,也许还没处理,也许在调用size()之后尺寸缩小了等等。fail-fast机制会探查容器上的任何除了你的进程所进行的操作以外的所有变化,一旦它发现其他进程修改了容器,立刻抛出ConcurrentModificationException异常,即快速报错——不适用复杂的算法在时候进行检查。

同步容器

同步容器可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。

同步容器将它们的状态封装起来,并对每一个公有方法进行同步。主要包括:

  • Vector
  • Stack
  • HashTable
  • Collections.synchronized方法生成,例如:
    • Collectinons.synchronizedList()
    • Collections.synchronizedSet()
    • Collections.synchronizedMap()
    • Collections.synchronizedSortedSet()
    • Collections.synchronizedSortedMap()

其中Vector(同步的ArrayList)和Stack(继承自Vector,先进后出)、HashTable(继承自Dictionary,实现了Map接口)是比较老的容器,Thinking in Java中明确指出,这些容器现在仍然存在于JDK中是为了向以前老版本的程序兼容,在新的程序中不应该在使用。Collections的方法时将非同步的容器包裹生成对应的同步容器。

同步容器在单线程的环境下能够保证线程安全,但是通过synchronized同步方法将访问操作串行化,导致并发环境下效率低下。而且同步容器在多线程环境下的复合操作(迭代、条件运算如没有则添加等)是非线程安全,需要客户端代码来实现加锁。

代码示例:

public static Object getLast(Vector list) {
    int lastIndex = list.size() - 1;
    return list.get(lastIndex);
}

public static void deleteLast(Vector list) {
    int lastIndex = list.size() - 1;
    list.remove(lastIndex);
}

上面的代码取最后一个元素或者删除最后一个元素,使用了同步容器Vector。如果有两个线程A,B同时调用上面的两个方法,假设list的大小为10,这里计算得到的lastIndex为9,线程B首先执行了删除操作(多线程之间操作执行的不确定性导致),而后线程A调用了list.get方法,这时就会发生数组越界异常,导致问题的原因就是上面的复合操作不是原子操作,这里可以通过在方法内部额外的使用list对象锁来实现原子操作。

在多线程中使用同步容器,如果使用Iterator迭代容器或使用使用for-each遍历容器,在迭代过程中修改容器会抛出ConcurrentModificationException异常。想要避免出现ConcurrentModificationException,就必须在迭代过程持有容器的锁。但是若容器较大,则迭代的时间也会较长。那么需要访问该容器的其他线程将会长时间等待。从而会极大降低性能。

此外,隐式迭代的情况,如toString,hashCode,equalse,containsAll,removeAll,retainAll等方法都会隐式的Iterate,也可能抛出ConcurrentModificationException。

并发容器

由上面的分析我们知道,同步容器并不能保证多线程安全,而并发容器是针对多个线程并发访问而设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,极大的提升同步容器类的性能。

ConcurrentHashMap

CopyOnWriteArrayList

  • 对应的非并发容器:ArrayList
  • 目标:代替Vector、synchronizedList
  • 原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。

关于这一部分可参考【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析

CopyOnWriteArraySet

  • 对应的费并发容器:HashSet
  • 目标:代替synchronizedSet
  • 原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

关于这一部分可参考【JDK】:CopyOnWriteArrayList、CopyOnWriteArraySet 源码解析

ConcurrentSkipListMap

  • 对应的非并发容器:TreeMap
  • 目标:代替synchronizedSortedMap(TreeMap)
  • 原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过”空间来换取时间”的一个算法。ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表。内部是SkipList(跳表)结构实现,在理论上能够在O(log(n))时间内完成查找、插入、删除操作。

ConcurrentSkipListSet

  • 对应的非并发容器:TreeSet
  • 目标:代替synchronizedSortedSet
  • 原理:内部基于ConcurrentSkipListMap实现

ConcurrentLinkedQueue

不会阻塞的队列

  • 对应的非并发容器:Queue
  • 原理:基于链表实现的FIFO队列(LinkedList的并发版本)

LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue

  • 对应的非并发容器:BlockingQueue
  • 特点:拓展了Queue,增加了可阻塞的插入和获取等操作
  • 原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
  • 实现类:
    • LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
    • ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
    • PriorityBlockingQueue:按优先级排序的队列

参考:
1、Java并发:同步容器&并发容器
2、Java并发:线程安全的容器-同步和并发

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【JDK】:Java容器框架——同步容器与并发容器 的相关文章

  • JDK8 JVM 简单堆分配实验

    实验环境 JDK8 Eclipse Neon 1a Release 4 6 1 实验问题 在进行新生代的from和to空间分配的配置时 发现分配的空间大小不对 实验内容 jvm参数 Xmx20m Xms20m Xmn1m XX Surviv
  • [小白进阶日记]JDK12 ZIP版配置教程

    JDK12 ZIP版配置教程 JDK 12 ZIP版配置教程 JDK 12 ZIP版配置教程 JKD下载地址 https download csdn net download qq 41684083 11472693 1 首先解压压缩包 2
  • JDK、JRE、JVM三者之间的关系

    jdk java development kit java开发工具包 jre java runtime environment java运行时环境 jvm java virtual machine java虚拟机 jdk环境配置 jdk环境
  • Centos7安装jdk1.8

    Centos7安装jdk1 8 两种安装方式 1 检查本机是否安装了jdk 安装了就不用安装了 命令如下 root localhost java version bash java 未找到命令 root localhost 注意 说明未安装
  • Java并发包中那些值得学习的并发工具类(空谈误国,实干兴邦,代码示范,抛砖引玉)

    首先我们通常说的并发包就是java util concurrent包及其子包 集中了Java并发的各种基础工具类 一 这个并发包在哪 上面的包就是传说中的并发包 为什么这个并发包就比较流弊呢 原因主要有以下几点 提供了几个比synchron
  • linux下jdk的安装

    目录 获取文件下载地址 官网获取文件下载地址 下载文件到指定目录下并修改改文件名 卸载已经存在的JDK 查看系统是否安装JDK 卸载JDK 安装JDK 赋予权限 安装JDK 配置JDK的环境变量 在配置文件的最底部加上以下配置 重新刷新配置
  • Mac下jdk的安装路径

    http hi baidu com liouyan9 blog item 78fdc009b97bdac63ac76377 html Mac下jdk的安装路径 2009 08 11 15 39 苹果系统已经包含完整的J2SE 其中就有JDK
  • 将 Tocmat5.0 注册为 Windows 的服务程序

    将 Tocmat5 0 注册为 Windows 的服务程序 步骤 1 下载 Tomcat 5 0 x 不要下载安装版本 2 解压到 TOMCAT HOME 3 安装或者从别处拷贝JRE 推荐拷贝 可以删除不需要的文件 如文档等 4 在 TO
  • 深度解析开源IDE的领头羊—【Eclipse 】--认识Eclipse

    认识Eclipse 阅读本文前 相信读者已经对Eclipse略知一二了 起码已经知道它是一个Java的集成开发环境 IDE 并且还是一个应用程序框架 可以通过开发插件 把Eclipse打造成各种应用软件 而且还打算通过学习本书的内容 在具体
  • java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment

    最近做项目遇到场景 通过前端提交的JAVA代码 创建java文件 然后再编译JAVA文件称class文件 具体实现的细节 就不细说了 最后执行的结果能正常执行 但控制台报错 Can t initialize javac processor
  • 聊聊你不知道的Java变量转型

    单枪匹马你别怕 一腔孤勇又如何 这一路你可以哭 但不能怂 请关注 源码猎人 目录 简介 标识符命名规则 类变量 静态变量 实例变量 局部变量 变量数据类型 基本类型之间的转换 常见面试题 简介 Java变量分为类变量 实例变量 局部变量 在
  • centos7 安装jdk17

    默认情况下 yum 仓库中是没有jdk 17 的 只有jdk 11 所以我们不能直接用yum 安装 需要手动下载进行配置工作 下载文件 wget https download oracle com java 17 latest jdk 17
  • 解决“The method XXXXXX of type XXXXXXXXX must override a superclass method”

    我的Eclipse版本是3 6 1 Override 时出现以下错误 The method XXXXXX of type XXXXXXXXX must override a superclass method 上网搜索原来原因是 实现类里面
  • 浅谈Class.forName()在JDBC中的作用

    目录 1 Class forName 有什么作用呢 2 为什么不直接new 3 为什么删除Class forName com mysql jdbc Driver 还是可以运行 JDBC是Bridge模式的典型应用 DriverManager
  • Android开发环境的搭建

    Android开发环境的搭建 在开始Android开发之旅启动之前 首先要搭建环境 然后创建一个简单的HelloWorld 本文的主题如下 1 环境搭建 1 1 JDK安装 1 2 Eclipse安装 1 3 Android SDK安装 1
  • JAVA之旅的第一步(安装JDK与JRE以及环境变量和IDEA)

    作为一名想要成为程序员的小白 我们开始肯定要学会一门语言 JAVA作为目前最为主流的编程语言 肯定是大家都要去学习的 那么如何开始我们的第一步呢 一 JDK与JRE的安装 首先我们要先安装JAVA 打开下面这个网址 注册你的Oracle账号
  • jdk8源码之Queue-ArrayQueue

    关于队列这个数据结构 大家应该都是比较熟悉 列队是一种先进先出 FIFO 的数据结构 删除操作只能在表的头部 插入操作只能在表的尾部 Queue一般是作为一个缓冲队列使用的 简单举例 生产端的生产速度偶尔会大于消费端的消费速度 但又不想等待
  • AIX5.3 weblogic9.2速度很慢问题的解决过程

    问题描述 开始的时侯采用的是32位JDK 但是部署应用的时侯总是out of memory 后来改用64位JDK 使用64位JDK之后没有aix 64位 native lib 最后从BEA工程师那取得了64位lib依然无法使用 没有使用na
  • 如何在 Ubuntu 22.04|20.04|18.04 上安装 Oracle Java 17

    Java 17 是 Java SE 平台的最新长期支持版本 由于这是 LTS 版本 因此将支持到 2024 年 9 月 Java 17 进行了多项改进和错误修复 工作站用户应考虑升级到此版本 此外 生产用户可以在检查所有应用程序兼容性检查后
  • 服务器乱码专栏问题一:String.getBytes()获取值乱码

    本文建议阅读时长 15mins 前记 最近笔者工作比较繁忙 导致本来应该有很多博客需要填坑 一直也没有静下心来好好整理下 今天蹭着夜深人静加之阵阵柔和的轻音乐正好先写上一篇 也算是为自己乱码专栏开个头 当然本专栏主要解决Tomcat服务器以

随机推荐

  • MarkerView

    前言 过了一个愉快的五一后 我们又开始上班了 完成了本分的工作 抽点时间来和说说MarkerView咯 给我的印象 MarkerView的扩展性很强 它可以自定义自己想要的U样式 MarkerView源码 View that can be
  • 【Spring】一篇文章快速搞懂BeanFactory和FactoryBean的区别

    目录 一 BeanFactory 1 1 源码 1 2 使用场景 二 FactoryBean 2 1 源码 2 2 示例 2 2 1 方法一 2 2 2 方法二 2 3 FactoryBean的两种用法 2 3 1 简化xml配置 隐藏细节
  • C语言结构体所占用的字节数如何计算

    结bai构体的数据类型的有点多我们就不啰嗦了 直du接来看相同数据结构体的几种书zhi写的格式吧 格式一 struct tagPhone char A int B short C Phone 格式二 struct tagPhone char
  • 前端模块化,基础组件和业务组件的区分

    前言 最近做的项目整个感觉就很混乱 经常性的c v 个人认为写代码就必须拥有很强的复用性 最近思考了许久 当前我们做的项目最缺少的就是模块化 及业务和基础的区分 模块化是一种思想 是将大工程拆成小的模块分治的思想 日常理解的模块化个人认为好
  • 【运维工程师学习四】Web服务之Linux配置安装Apache

    运维工程师学习四 Web服务之Apache 1 查询 安装apache rpm命令使用 2 验证httpd是否启动成功 1 查看是否有httpd的进程 2 查看是否有80端口在监听中 3 CentOS7默认不带netstat命令 通过yum
  • 浅谈 vue路由跳转的四种方式

    ps 区别 this router push 跳转到指定url路径 并想history栈中添加一个记录 点击后退会返回到上一个页面 this router replace 跳转到指定url路径 但是history栈中不会有记录 点击返回会跳
  • 使用 Python 将若干个 PDF 文件合并到一个中

    from pathlib import Path from PyPDF2 import PdfFileMerger read path r C Users liujieru Documents 需要合并的 pdf所在文件夹 write pa
  • Pinia

    store 是一个用reactive 包裹的对象 这意味着不需要在getter 之后写 value 但是 就像setup 中的props 一样 我们不能对其进行解构 大多数时候 getter 只会依赖状态 但是 他们可能需要使用其他 get
  • 使用webstorm操作git

    前言 对于git的使用 大家的使用方式均有不同 最王道的方式非命令行莫属 基于git的GUI软件还是很多的 大家可自行研究使用 之前使用eclipse svn插件去操作版本管理 还是很便捷的一件事情 而今用惯了webstorm 当然里面也集
  • 其中的文件夹或文件已在另一个程序中打开怎么解决

    不小心下载流氓软件 卸载时候总出现文件夹或文件正在被占用 出现这种方法 我在网上查都是这种解决方法 如下 方法一 win R 快捷键打开运行窗口 并输入taskmgr 打开任务管理器 点击 性能 点击 打开资源监视器 点击 CPU 然后在关
  • vite.config.js-element-plus

    打包工具 webpack 一 根据element plus官网给的方法配置自动导入 1 首先你需要安装unplugin vue components 和 unplugin auto import这两款插件 npm install D unp
  • 应用场景是什么?怎样判断、描述一个产品的应用场景?

    注 论文发表于 包装工程 杂志2017年第6期 工业设计 栏目 虽然是17年才发表 但这篇论文其实是很早之前写的 所以用的案例比较老 作者 胡伟峰 王玉梅 汤进 李世国 江南大学 无锡 214122 摘 要 目的 研究产品交互设计中场景理论
  • Springboot 集成logback 日志框架简介

    Spring Boot 作为微服务应用 默认集成 logback 日志框架 logback 是log4j框架的作者开发的新一代日志框架 它效率更高 能够适应诸多的运行环境 同时天然支持SLF4J 笔者刚好接触SpringBoot不久 之前用
  • AndroidStudio解决Gradle文件占用的问题

    在使用AndroidStudio开发时 经常在修改代码之后编译出现文件占用的问题 具体的提示一般是 xxx jar classes jar等出现文件占用 无法编译 传统的办法一般是 重启AndroidStudio 或者通过任务管理器搜索占用
  • QT DAY2

    华清远见上海中心22071班
  • NV21、NV12、YV12、RGB565、YUV等颜色编码格式区别和接口设计探讨

    NV21 NV12 YV12 RGB565 YUV扫盲 NV21 NV12 YV12 RGB565 YUV分别是不同的颜色编码格式 这些颜色编码格式各有特点 适用于不同的应用场景 选择合适的颜色编码格式取决于具体的需求和环境 NV21 NV
  • FastDFS安装步骤以及SpringBoot集成fastdfs-client-java

    一 环境准备 1 前期准备 名称 说明 centos 7 x libfastcommon FastDFS分离出的一些公用函数包 FastDFS FastDFS本体 fastdfs nginx module FastDFS和nginx的关联模
  • Ubuntu18.04下安装配置SSH服务

    安装ssh工具 1 打开终端键入如下命令 apt get update apt get install openssh server 2 选择Y继续执行 启动SSH服务 1 键入如下命令 etc init d ssh start 注 重启命
  • 机器学习面试题汇总(1~50题)

    机器学习面试题汇总 1 50题 1 深度神经网络预防过拟合的方法 2 SMOTE算法 过采样算法 3 为什么LR 逻辑回归 用sigmoid函数 4 LR损失函数 5 几种神经网络梯度下降方法 6 克莱姆法则 7 各种排序的时间复杂度 8
  • 【JDK】:Java容器框架——同步容器与并发容器

    前面的文章中详细介绍了Java的容器框架 在此基础上 本文对Java中的同步容器与并发容器做一些介绍 fail fast机制 快速报错机制 fail fast 能够防止多个进程同时修改同一个容器的内容 如果在你迭代遍历某个容器的过程中 另一