Java同步集合synchronizedX中的迭代器Iterator使用,为什么需要使用者加锁?

2023-05-16

(尊重劳动成果,转载请注明出处:https://yangwenqiang.blog.csdn.net/article/details/129472842冷血之心的博客)

前言

大家好,我是冷血之心。上个博客中,我宣布正式回归博客,接下来我持续分享一些有用的知识点,做到共同进步、共同成长。
好了,废话不多说了,我们开始今天的主题:《Java同步集合synchronizedX中的迭代器Iterator使用,为什么需要使用者加锁?》

备注

查看本文需要一定的Java集合、线程安全知识,本文不是一篇科普文,本文的目的是和大家讨论一个容易被忽略的问题,引导大家思考。

正文

我们先来看一些基本的概念。

常见集合

集合

常见的集合包括List、Set、Map等,List和Set集群的父接口是Collection接口。从实现类上看,我们常见的实现类包括:

  • ArrayList、LinkedList
  • HashSet、TreeSet
  • HashMap、LinkedHashMap

以上的常见集合都是线程不安全的。

同步集合

那么什么是同步集合?

同步集合是指通过java.util.Collections.synchronizedX(其中,X可以是Set、List、Map等)方法,通过装饰器模式将指定的非线程安全的集合对象对外暴露为线程安全的对象(外包装对象)。这个方法将返回指定集合的外包装对象,这些集合称之为同步集合(Synchronized Collection)。

并发集合

在JDK1.5中引入了java.util.concurrent包,在该包中定义了一组线程安全的集合,称为并发集合,这些集合可以作为同步集合的替代品。在这里插入图片描述

并发集合是怎么做到线程安全遍历的?

首先,上述的并发集合本身就支持对其线程安全的遍历,其实现方式一般有两种:

  • 快照方式:对待遍历对象的快照进行遍历
  • 准实时方式:准实时是指遍历操作不是针对待遍历对象的副本进行的,但又不借助锁来保障线程安全,从而使得遍历操作可以与更新操作并发进行。

那么准实时是怎么实现的?

  • 常见的准实时遍历方式包括CAS、使用粒度极小的锁

我们简单看下CopyOnWriteArrayList的源码实现吧。

基本元素:
在这里插入图片描述

读取操作:
在这里插入图片描述

更新操作:(增、删、改)
在这里插入图片描述

遍历操作:
在这里插入图片描述
好了,类似使用场景等更多的分析我们就不说了。都是一些比较基础的理解,大家在书本以及别的博客上已经看了无数遍了。

同步集合是怎么实现线程安全的?

我们首先从大的方面说:同步集合是通过装饰器模式来实现线程安全的。

简单点理解就是通过在非线程安全的集合对象外边包装一层,通过加互斥锁的方式来实现。客户端代码直接访问线程安全的外包装对象,外包装对象通过互斥锁提供了线程安全的访问方式。并且,外包装对象内部直接引用了内部集合对象的接口,所以两者具有相同的接口。

我们来简单看下源码实现:
在这里插入图片描述

基本元素:
在这里插入图片描述

接口实现:
在这里插入图片描述
OK,看到这里,你也许会想,这不是很简单么,我已经得到了真传,无非就是加锁实现呗,但是这样肯定会降低并发度的。
是的,你说的很有道理,但是你查阅别的资料的时候,经常会看到这么一段分析:

同步集合的iterator方法返回的Iterator实例并不是线程安全的。为了保障对同步集合的遍历操作的线程安全性,我们需要对遍历操作进行加锁

如下所示:

public class SyncCollectionSafeTraversal {
    final List<String> syncList = Collections.synchronizedList(new ArrayList<>());
    public void test(){
        Iterator<String> iterator = syncList.iterator();
        //需要对该对象进行加锁,因为返回的Iterator是非线程安全的,降低了并发性能
        synchronized (syncList){
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
    }
}

前面不都说了,同步集合是在非线程安全的集合外层通过互斥锁包装了一层么,为啥遍历器又成了非线程安全的了?

我们再来看下iterator实现源码:在这里插入图片描述
嗯?作者竟然真的没有加互斥锁,还特别标注了请使用者自行保证同步。
奇怪了,为啥作者就不加锁了呢?作者是不是有啥难言之隐?

我们来简单分析下:
如果iterator方法上增加互斥锁是否可以实现线程安全

类似这样:

public Iterator<E> iterator() {
        synchronized (mutex) {
            return c.iterator();
        }
    }

答案显然是不行的。为啥不行呢?

在iterator方法上使用了互斥锁,只能保证我们获取迭代器对象是线程安全的,也就是说只有一个线程会获取迭代器对象。然后呢?我们获取到迭代器对象是需要遍历操作的,在这个过程中,依然会有其余线程不断的对同步集合进行增删改操作。所以在我们迭代遍历的过程中就会出错,所以是无法保证线程安全的。

OK,说到这里,我们解释了本文标题的问题,也就是为什么Java同步集合synchronizedX中的迭代器Iterator使用,需要使用者加锁?

那么我们再来看一下,如果加一把普通的锁可以吗?如下所示:

public class SyncCollectionSafeTraversal {
    final List<String> syncList = Collections.synchronizedList(new ArrayList<>());
    public void test(){
        Iterator<String> iterator = syncList.iterator();
        // 加了一把普通的内部锁(这是锁不住滴!!!)
        synchronized (new Object()){
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
        }
    }
}

答案必然也是不可以的?为啥呢?

因为我们需要保证在迭代器遍历的过程中,集合中的元素不会被增删改,这里加一把普通的内部锁,无法锁住当前同步集合上的增删改操作,也就无法保证集合遍历操作的线程安全性。

正确的使用方法如上上边代码所示,我们应该使用被遍历的同步集合对象本身作为内部锁,利用内部锁的排他性,从而阻止了遍历过程中其他线程改变了同步集合的内部结构。

总结

这篇文章,我们简单分析了Java同步集合在遍历的时候应该如何做好线程安全。知识点很小,主要的目的是引导大家思考。

我们平时看了太多的八股文了,都是简简单单的说同步集合的遍历需要自己加锁,但是鲜有文章会具体阐述为什么需要加锁?应该怎么加锁?为什么不从源码上来保证迭代的线程安全性?所以,在日常的学习中,我们要多加思考,不能死记硬背,要结合实践来理解。

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

Java同步集合synchronizedX中的迭代器Iterator使用,为什么需要使用者加锁? 的相关文章

  • Codeblocks注释快捷键

    语句块 xff1a 加上注释 ctrl 43 shift 43 C 去掉注释 ctrl 43 shift 43 X
  • 导入import cv2时出现ImportError:DLL load fail:找不到指定模块的解决办法

    一 Python上安装opencv 遇到的问题 xff1a 前几天在导入import cv2的时候 xff0c 出现 ImportError DLL load fail 找不到指定模块 xff0c 我是按照pip install openc
  • C语言中数组长度的计算详解

    一 C语言中计算数组长度大小 C语言字符串长度的计算可以使用strlen str 但是对于数组长度的大小却没有相关函数可以使用 xff1b C语言数组长度的大小可以使用 xff1a span class token keyword int
  • VS对文件编码格式的转换方法

    前言 最近 xff0c 在Jupyterlab上遇到了这样的情景 xff1a 就是说呢 xff0c 这个文件不是UTF 8编码格式的文件 xff0c 它识别不了内容 xff0c 打不开 所以 xff0c 咱们需要对这些文件进行编码格式的转换
  • python 正则 .+?与.*?的区别 (正则表达式)

    匹配除 r n 之外的任何单个字符 要匹配包括 r n 在内的任何字符 xff0c 请使用像 s S 的模式 xff1f 匹配前面的子表达式0到1次 匹配前面的子表达式人任意次 匹配前面的子表达式一次或多次 gt 61 1 xff09 xf
  • 程序员编程中遇到的那些灵异事件,胆小误入!

    某公司有个码农工作压力太大 xff0c 天天晚上加班到半夜 xff0c 最后受不了跳楼死了 xff0c 他的机位从此就一直空着 但令大家都感到非常奇怪的是 xff0c 有几次早上来上班时却发现这台机子竟然开着 xff01 大概是因为电源有问
  • MQTT是什么

    MQTT是什么 xff1f MQTT xff08 Message Queuing Telemetry Transport xff0c 消息队列遥测传输协议 xff09 xff0c 是一种基于发布 订阅 xff08 Publish Subsc
  • 进程唤醒

    xff08 进程的操作 xff0c 都是有原语 xff08 函数 xff0c 具有原子性 xff09 去执行的 xff09 圈起来 xff0c 以后会考 进程创建参数 xff1a 进程标识 xff0c 优先级 xff0c 进程起始地址 xf
  • mqtt v5.0请求响应式 Request/Response

    MQTT v5 MQTT 5 0 新特性介绍 项目中逐步完成了 MQTT 5 0的开发 xff0c 这里介绍下MQTT 5 0 的一些新特性 MQTT 5 0 规范见 xff1a http docs oasis open org mqtt
  • ffmpeg + RTSP服务器实现推流和拉流

    1 引 言 在项目开发过程中经常遇到接入RTSP视频流的需求 由于外网无法访问项目现场的内网服务器 xff0c 无法进行测试 想着在网上找一个RTSP流进行测试 xff0c 但是大多已经失效 xff0c 且公司对网络进行了限制 xff0c
  • linux c++11 获取本机ip地址 ipv4

    span class token macro property span class token directive hash span span class token directive keyword include span spa
  • ModbusTCP协议详解

    阿富哥带你详细了解modbus tcp规范 1 该规范的发展概况 该MODBUS TCP规范在万维网上公开发行 它表明开发者的意愿是把它作为工业自动化领域具有互用性的标准 既然MODBUS和MODBUS TCP作为事实上的 实际 标准 xf
  • 闲谈5个改变未来的人工智能技术(CV方向)

    前言 计算机视觉 xff08 CV xff09 一直是目前深度学习领域最热的研究领域 xff0c 其是一种交叉学科包括计算机科学 xff08 computer science Graphics Algorithms Theory Syste
  • 基于深度学习的行人多目标跟踪方法

    文章目录 基于深度学习的行人多目标跟踪方法0 引 言1 基于检测的跟踪基本概念2 行人多目标检测与跟踪2 1 目标检测2 1 1 基于 Faster R CNN 的目标检测2 1 2 基于 SSD 的目标检测2 1 3 基于 YOLO 的目
  • Java中字符串匹配算法

    什么是字符串匹配 字符串匹配是主串返回模式串在主串中出现的位置 xff0c 类似于mysql中FIND IN SET LOCATE POSITION INSTR等函数的作用 比如主串 xff1a abbcefgh xff0c 模式串 xff
  • java集合的交集,并集,差集

    原文地址 http blog csdn net qq 25806863 article details 70312046 今天要用到差集 xff0c 突然懵逼了 然后脑子一抽写出了下面的代码 Set lt String gt set1 61
  • Ubuntu的linux中shell和gnome分别所在根文件系统中的./bin和./etc文件夹下

    Linux根文件系统中一般有如下的几个目录 xff1a 1 bin目录该目录下的命令可以被root与一般账号所使用 xff0c 由于这些命令在挂接其它文件系统之前就可以使用 xff0c 所以 bin目录必须和根文件系统在同一个分区中 bin
  • c语言1左移i 什么意思

    c语言1 lt lt i 什么意思 5 我来答 分享 举报浏览 4917 次 3个回答 网络热词科普 D amp G是什么意思 xff1f 最佳答案 天云一号 推荐于2017 09 09 C语言中1 lt lt i表示将1的二进制每位想左移
  • python如何实现网页爬取,并翻译成中文

    曾经有个想法 xff0c 是否可以通过抓取英语新闻 xff0c 来做一个国人看得懂的国际新闻资讯APP xff0c 如今闲暇之余 xff0c 实现了 简单而快乐 现贴出代码分享给你们 xff0c 做个参考 import io import
  • N1刷入Armbian后换软件源、安装Docker并提速

    N1刷入Armbian后换软件源 安装Docker并提速 文章转自恩山原地址 xff1a N1刷入Armbian 网络环境所限 xff0c 在使用Armbian的时候经常下载软件出问题 xff0c 下载不到或者下载中断 xff0c 所以找来

随机推荐

  • 【第四章:网络协议】第19节:网络协议 - 入门(下)

    大家好 上一小节我们介绍了一些基础的网络协议和面试中的高频考点 本节我们继续介绍基础网络协议相关知识点 主要知识点包括HTTP和HTTPS相关协议 路由汇聚以及子网掩码的求法等 1 HTTP和HTTPS的区别有哪些 掌握 答 HTTP和HT
  • 【第四章:网络协议】第20节:Web开发安全

    大家好 在前面两个小节中 我们对网络协议相关的知识点做了简单的介绍 本小节是网络协议篇的最后一节 主要介绍和Web开发相关的安全漏洞 包括XSS跨站脚本攻击 CSRF跨站请求伪造 SSRF服务端请求伪造以及SQL注入漏洞等 XSS CSRF
  • 【第五章:MySQL数据库】第21节:MySQL - 必知必会(上)

    大家好 很高兴我们可以继续学习交流Java高频面试题 第五章主要是对MySQL数据库进行浅析 我们主要针对面试中常见的MySQL知识点进行交流分析 在对高频面试题分析的过程中 加入实际工作中经常需要使用到的MySQL优化等知识 希望大家可以
  • 【第五章:MySQL数据库】第22节:MySQL - 必知必会(下)

    大家好 上一小节中我们对MySQL中的索引和存储引擎做了简单的介绍 本小节中 我们接着学习MySQL相关知识点 包括日志模块 锁机制以及事务等重要知识点 希望大家可以有效理解与掌握 1 MySQL的日志模块binlog和redo log有了
  • 【第六章:常用工具命令】第23节:常用命令 - Linux命令

    大家好 很高兴我们可以继续学习交流Java高频面试题 本小节开始 我们进入了常用工具命令篇章 我们分三个小节来交流常见工具的命令及其作用 包括Linux常用命令 Maven常用命令以及Git常用命令 本小节中 我们主要针对面试中以及日常开发
  • 【第六章:常用工具命令】第24节:常用命令 - Maven命令

    大家好 上一小节中我们主要对Linux常用的命令做了介绍 本小节中 我们对Maven相关知识点以及其常用命令进行交流与学习 Maven是什么 掌握 Maven 是一个跨平台的强大构建工具 可以实现自动化构建过程 从清理 编译 测试 生成报告
  • 【第六章:常用工具命令】第25节:常用命令 - Git命令

    大家好 前面两节我们分别介绍了Linux和Maven的常用命令 本小节中 我们继续介绍版本控制工具Git的相关知识点及其常用命令 Git是一个优秀的版本控制管理工具 可以帮助我们进行代码版本的管理 目前是市场上主流的版本管理工具 如果你拥有
  • 一种灵活的活动中前后端配置数据交互方式

    技术问题 xff1a 在活动中一般会有非常频繁的配置类数据 xff0c 这些数据基本上不包含具体的业务处理逻辑 xff0c 多数是为了前端渲染活动页面使用 传统的前后端交互中 xff0c 往往是前端需要某些配置类数据 xff0c 然后服务端
  • 【第七章:高频算法】第26节:高频面试算法 - 基础(上)

    大家好 很高兴我们可以继续学习交流Java相关面试题目 本小节开始 我们主要进行高频算法题目的讲解 手撕算法 应该算是技术岗位最通用的面试题目了 在各大公司的面试中 有一个最基本的要求 那就是必须写点代码 技术面试一般情况下可以归纳为三大块
  • 【第七章:高频算法】第27节:高频面试算法 - 基础(下)

    大家好 很高兴我们可以继续交流学习算法相关的面试题 在上一小节中 我们主要对排序与查找算法 常见链表以及二叉树的面试题目进行了分析与交流 在本小节中 我们主要对队列 堆栈 字符串与数组等知识点进行交流 针对各个知识点最高频的面试题目来进行解
  • laravel 导出文件乱码解决

    最近做导出功能 xff0c 发现导出的文件名出现乱码 查阅资料发现 xff0c 头部没有统一的编码 xff0c 需要对不同的浏览器做兼容处理 在此记录一下 xff0c 解决方法 xff0c 可以参考一下 修改下载导出的头部信息 functi
  • 【第八章:设计模式】第28节:面试常考设计模式 (上)

    大家好 从本小节开始我们一起交流常用的设计模式 设计模式是一种思想 并不是一门具体的技术 没有很多的工作积累是不可能真正理解设计模式的 本专刊中的设计模式章节 我们重点阐述面试中常见的设计模式的原理与使用 设计模式就是在软件开发过程中所总结
  • 【第八章:设计模式】第29节:面试常考设计模式(下)

    大家好 本小节中我们继续交流学习常见的设计模式 在上一小节中 我们介绍了设计模式的六大原则 并且重点阐述了单例模式的多种写法 单例模式也是为数不多的可以在面试中直接 手撕 的设计模式 除了单例模式外 在面试中考察的其余常见设计模式一般都不会
  • 【第九章:面试分享】第30节:应聘者角度分享面经与面试秘籍

    大家好 非常感谢大家可以和我一起进行交流学习 在前面8个章节中 我竭尽所能的对Java开发岗的常见重要知识点进行了梳理总结 力争用最通俗易懂的语言表达出来 使得大家可以轻松掌握其技术原理 在第九章中 我将进行一些面试分享 本小节中 我将和大
  • 【第九章:面试分享】第31节:面试官角度分享面试与学习方法

    大家好 上一小节我们分享了一些真实的实习和校招面经 并且讲述了作为应聘者如何高效准备和应对面试 本小节 我转变身份 站在面试官的角度来分享总结面试 本小节的主要内容如下 面试中 大多数应聘者是如何表现的 作为面试官的我如何考察应聘者 一些重
  • 【加餐篇:社招分享】第32节:工作两年,大厂社招Offer拿到手软

    大家好 在专刊初版完结多日之后 我们又见面了 本专刊的主题是Java开发岗位高频面试题解析 力争从多个知识模块上进行总结 以一种全而精的方式来给大家介绍相关知识点以及其实现原理 回答话术等 相信本专刊对于大家的学习与面试都有一个很大的帮助
  • Git:一文阐述常见命令的使用

    前言 为什么写这篇Git文章 xff1f 在日常的需求开发中 xff0c 发现部分同学不太熟悉Git命令 xff0c 往往是通过idea自带的一些工具来执行简单的Git命令 xff0c 遇到一些突发问题的时候 xff0c 往往不知所措 简单
  • 一种通用的静态资源发布方法

    技术问题 xff1a 在日常的用户页面中 xff0c 往往会存在一些常见问题页面 常见问题页面一般情况下分类 分条目的阐述当前用户侧容易遇到的一些问题 一般情况下 xff0c 问题会比较固定 xff0c 其对应的回答或者解释也比较固定 xf
  • 【回归贴】同志们,阔别三年,我回来啦~

    背景 熟悉的感觉 xff0c 接近三年没有好好写博客了 xff0c 因为各种各样的事情吧 在20年初跳槽到当前公司后 xff0c 因为工作繁忙等各种原因 xff0c 我几乎要放弃了写作 但是 xff0c 人不可能一直呆在舒适圈 xff0c
  • Java同步集合synchronizedX中的迭代器Iterator使用,为什么需要使用者加锁?

    xff08 尊重劳动成果 xff0c 转载请注明出处 xff1a https yangwenqiang blog csdn net article details 129472842冷血之心的博客 xff09 前言 大家好 xff0c 我是