Java 并发编程实战
王宝令
资深架构师
72485 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
学习攻略 (1讲)
Java 并发编程实战
15
15
1.0x
00:00/00:00
登录|注册

20 | 并发容器:都有哪些“坑”需要我们填?

课后思考
总结
并发容器
同步容器
Java并发容器

该思维导图由 AI 生成,仅供参考

Java 并发包有很大一部分内容都是关于并发容器的,因此学习和搞懂这部分的内容很有必要。
Java 1.5 之前提供的同步容器虽然也能保证线程安全,但是性能很差,而 Java 1.5 版本之后提供的并发容器在性能方面则做了很多优化,并且容器的类型也更加丰富了。下面我们就对比二者来学习这部分的内容。

同步容器及其注意事项

Java 中的容器主要可以分为四个大类,分别是 List、Map、Set 和 Queue,但并不是所有的 Java 容器都是线程安全的。例如,我们常用的 ArrayList、HashMap 就不是线程安全的。在介绍线程安全的容器之前,我们先思考这样一个问题:如何将非线程安全的容器变成线程安全的容器?
在前面《12 | 如何用面向对象思想写好并发程序?》我们讲过实现思路其实很简单,只要把非线程安全的容器封装在对象内部,然后控制好访问路径就可以了。
下面我们就以 ArrayList 为例,看看如何将它变成线程安全的。在下面的代码中,SafeArrayList 内部持有一个 ArrayList 的实例 c,所有访问 c 的方法我们都增加了 synchronized 关键字,需要注意的是我们还增加了一个 addIfNotExist() 方法,这个方法也是用 synchronized 来保证原子性的。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java并发容器包括同步容器和并发容器两大类。同步容器通过synchronized关键字实现线程安全,但性能较差。为提高性能,Java 1.5及之后版本提供了并发容器,包括List、Map、Set和Queue等类型。 同步容器可通过封装非线程安全容器并控制访问路径实现线程安全。Java提供Collections类包装非线程安全容器,如ArrayList、HashSet和HashMap,使其线程安全。然而,组合操作可能引发竞态条件问题,迭代器遍历容器时存在并发问题。 并发容器包括CopyOnWriteArrayList、ConcurrentHashMap、ConcurrentSkipListMap、CopyOnWriteArraySet、ConcurrentSkipListSet等实现类。这些容器通过不同机制实现线程安全和提高性能。例如,CopyOnWriteArrayList通过复制共享变量实现读操作无锁,但不支持迭代器的增删改操作。 总的来说,并发容器提供线程安全,但需注意适用场景、迭代器操作、性能特点等。文章详细介绍了同步容器和并发容器的特点和使用注意事项,对Java并发编程有参考意义。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(52)

  • 最新
  • 精选
  • 黑白尤文
    Java7中的HashMap在执行put操作时会涉及到扩容,由于扩容时链表并发操作会造成链表成环,所以可能导致cpu飙升100%。

    作者回复: 👍

    2019-04-13
    2
    175
  • ykkk88
    没有理解为什么concurrentskiplistmap比concurrenthashmap性能好

    作者回复: 如果key冲突比较大,hashmap还是要靠链表或者tree来解决冲突的,所以O(1)是理想值。同时增删改操作很多也影响hashmap性能。这个也是要看冲突情况。也就是说hashmap的稳定性差,如果很不幸正好偶遇它的稳定性问题,同时又接受不了,就可以尝试skiplistmap,它能保证稳定性,无论你的并发量是多大,也没有key冲突的问题。

    2019-04-13
    6
    92
  • 张天屹
    我理解的hashMap比其它线性容器更容易出问题是因为有扩容操作,存在更多竞态条件,所以如果条件满足时切换可能导致新生成很多数组,甚至可能出现链表闭环,这种情况可以查看堆栈,比如jstack查看会发现方法调用栈一直卡在HashMap的方法。另外上文迭代器遍历不安全是因为hasNext(size)和next()存在的竞态条件吗

    作者回复: 👍,不止是存在竞态条件,如果在遍历的时候出现修改操作,直接抛快速失败异常

    2019-04-13
    2
    47
  • WolvesLeader
    个人认为您第二篇内存模型讲的非常棒,,,,,,,,,,

    作者回复: 我觉得自己理解起来困难而且对实际工作还有用的就会讲的深入一些,反之我觉得概念或者工具跟正常思维没有冲突,就会讲的简单,甚至略过。毕竟我们只是工具的使用者,首要问题是利用这些工具解决问题。感谢你的认可,我甚至觉得写完第二篇和管程之后就可以收工了,其他所有章节不过就是帮助大家进一步理解,从不同角度理解。

    2019-04-13
    5
    39
  • 龙猫
    java8之前的版本hashmap执行put方法时会有环形链表的风险,java8以后改成了红黑树

    作者回复: 👍

    2019-04-18
    3
    34
  • CCC
    老师,用跳表实现的ConcurrentSkipListMap为什么可以做到无锁并发呢

    作者回复: 那个跳表就跟字典的索引一样,通过这个索引既能快速定位数据,也能隔离并发(可以并发查看不同页上的字)

    2019-04-13
    21
  • Liam
    LinkedTransferQueue有什么应用场景吗?

    作者回复: 实际工作中,为了防止OOM,基本上都使用有界队列,我工作中也没用过LinkedTransferQueue。

    2019-04-13
    19
  • QQ怪
    除了jdk8之前因为并发导致的链表成环的问题还有一种可能是因为jdk8之前hash冲突还是使用的是链表,而jdk8之后使用了红黑树,开始还是生成链表,当链表长度为8时就会转变为红黑树,时间复杂度为O(logn),比链表效果好的多。

    作者回复: 是的,底层实现变了,我同事在1.8版本费了好大劲都没重现出来

    2019-04-13
    2
    15
  • 罗洲
    jdk1.8以前的HashMap并发扩容的时候会导致陷入死循环,所以会导致cpu飙升,那么验证猜想我觉得有2种方法: 1.线上查故障,用dump分析线程。 2.用1.8以前的jdk在本地模拟。

    作者回复: 👍

    2019-04-13
    14
  • Randy
    留言中很多都提到在JDK1.8以前会存在HashMap的并发情况下resize可能导致死循环问题,其实这个问题在1.8中也存在,并没有因为在1.8中引入了红黑树而规避掉。因为导致问题的原因是resize方法调用了transfer,也就是说是发生在链表的重组过程,跟红黑树没有关系。所以JDK1.8中还是存在这个问题 请宝令老师指正

    作者回复: 刚好有篇公众号讲到这个问题,写的非常好。https://mp.weixin.qq.com/s/yxn47A4UcsrORoDJyREEuQ

    2019-11-14
    3
    11
收起评论
显示
设置
留言
52
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部