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

07 | 安全性、活跃性以及性能问题

总结
性能问题
活跃性问题
安全性问题
并发编程中的关键问题

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

通过前面六篇文章,我们开启了一个简单的并发旅程,相信现在你对并发编程需要注意的问题已经有了更深入的理解,这是一个很大的进步,正所谓只有发现问题,才能解决问题。但是前面六篇文章的知识点可能还是有点分散,所以是时候将其总结一下了。
并发编程中我们需要注意的问题有很多,很庆幸前人已经帮我们总结过了,主要有三个方面,分别是:安全性问题、活跃性问题和性能问题下面我就来一一介绍这些问题。

安全性问题

相信你一定听说过类似这样的描述:这个方法不是线程安全的,这个类不是线程安全的,等等。
那什么是线程安全呢?其实本质上就是正确性,而正确性的含义就是程序按照我们期望的执行,不要让我们感到意外。在第一篇《可见性、原子性和有序性问题:并发编程 Bug 的源头》中,我们已经见识过很多诡异的 Bug,都是出乎我们预料的,它们都没有按照我们期望的执行。
那如何才能写出线程安全的程序呢?第一篇文章中已经介绍了并发 Bug 的三个主要源头:原子性问题、可见性问题和有序性问题。也就是说,理论上线程安全的程序,就要避免出现原子性问题、可见性问题和有序性问题。
那是不是所有的代码都需要认真分析一遍是否存在这三个问题呢?当然不是,其实只有一种情况需要:存在共享数据并且该数据会发生变化,通俗地讲就是有多个线程会同时读写同一数据那如果能够做到不共享数据或者数据状态不发生变化,不就能够保证线程的安全性了嘛。有不少技术方案都是基于这个理论的,例如线程本地存储(Thread Local Storage,TLS)、不变模式等等,后面我会详细介绍相关的技术方案是如何在 Java 语言中实现的。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

并发编程中的安全性、活跃性和性能问题是关注的核心议题。安全性问题包括原子性、可见性和有序性问题,需要避免数据竞争和竞态条件,可通过锁机制解决。活跃性问题涉及死锁、活锁和饥饿,需要采取相应措施避免线程无法执行下去的情况。性能问题中提到了锁的过度使用可能导致串行化范围过大,影响多线程的优势,并介绍了如何避免锁带来的性能问题。文章还提到了性能方面的度量指标,包括吞吐量、延迟和并发量。总结指出并发编程是一个复杂的技术领域,需要重点关注安全性、活跃性和性能。文章以深入浅出的方式介绍了并发编程中的关键问题,为读者提供了全面的并发编程概览。

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

全部留言(165)

  • 最新
  • 精选
  • vector是线程安全,指的是它方法单独执行的时候没有并发正确性问题,并不代表把它的操作组合在一起问木有,而这个程序显然有老师讲的竞态条件问题。

    作者回复: 👍

    2019-03-14
    5
    197
  • kaixiao7
    老师,串行百分比一般怎么得出来呢(依据是什么)?

    作者回复: 你可以这么理解:临界区都是串行的,非临界区都是并行的,用单线程执行临界区的时间/用单线程执行(临界区+非临界区)的时间就是串行百分比

    2019-03-29
    3
    139
  • 飘呀飘的小叶子
    Vector实现线程安全是通过给主要的写方法加了synchronized,类似contains这样的读方法并没有synchronized,该题的问题就出在不是线程安全的contains方法,两个线程如果同时执行到if(!v.contains(o)) 是可以都通过的,这时就会执行两次add方法,重复添加。也就是老师说的竞态条件。

    作者回复: 👍

    2019-03-14
    6
    86
  • 虎虎❤️
    老师讲的太好了。我没有并发的编程经验,但是可以看懂每一篇文章,也可以正确回答每节课后的习题。我觉得这次跟对了人,觉得很有希望跟着老师学好并发。 但是,这样跟着学完课程就能学好并发编程吗?老师可以给些建议吗?除了跟着课程,我还需要做些什么来巩固战果?老师能不能给加餐一篇学习方法,谢谢! 本节课总结: 安全性: 数据竞争: 多个线程同时访问一个数据,并且至少有一个线程会写这个数据。 竞态条件: 程序的执行结果依赖程序执行的顺序。 也可以按照以下的方式理解竞态条件: 程序的执行依赖于某个状态变量,在判断满足条件的时候执行,但是在执行时其他变量同时修改了状态变量。 if (状态变量 满足 执行条件) { 执行操作 } 问题: 数据竞争一定会导致程序存在竞态条件吗?有没有什么相关性? 活跃性: 死锁:破坏造成死锁的条件,1,使用等待-通知机制的Allocator; 2主动释放占有的资源;3,按顺序获取资源。 活锁:虽然没有发生阻塞,但仍会存在执行不下去的情况。我感觉像进入了某种怪圈。解决办法,等待随机的时间,例如Raft算法中重新选举leader。 饥饿:我想到了没有引入时间片概念时,cpu处理作业。如果遇到长作业,会导致短作业饥饿。如果优先处理短作业,则会饿死长作业。长作业就可以类比持有锁的时间过长,而时间片可以让cpu资源公平地分配给各个作业。当然,如果有无穷多的cpu,就可以让每个作业得以执行,就不存在饥饿了。 性能: 核心就是在保证安全性和活跃性的前提下,根据实际情况,尽量降低锁的粒度。即尽量减少持有锁的时间。JDK的并发包里,有很多特定场景针对并发性能的设计。还有很多无锁化的设计,例如MVCC,TLS,COW等,可以根据不同的场景选用不同的数据结构或设计。 最后,在程序设计时,要从宏观出发,也就是关注安全性,活跃性和性能。遇到问题的时候,可以从微观去分析,让看似诡异的bug无所遁形。

    作者回复: 能看懂说明基本功很扎实啊。你的建议我会考虑的。

    2019-03-14
    2
    75
  • 寒铁
    add10K() 如果用synchronized修饰 应该就没有问题了吧? get和set是synchronized不能保证调用get和set之间的没有其他线程进入get和set,所以这是导致出错的根本原因。

    作者回复: 👍

    2019-04-03
    36
  • Nevermore
    编写并发程序的初衷是为了提升性能,但在追求性能的同时由于多线程操作共享资源而出现了安全性问题,所以才用到了锁技术,一旦用到了锁技术就会出现了死锁,活锁等活跃性问题,而且不恰当的使用锁,导致了串行百分比的增加,由此又产生了性能问题,所以这就是并发程序与锁的因果关系。

    作者回复: 👍

    2019-03-14
    3
    33
  • 亮亮
    void addIfNotExist(Vector v, Object o){ synchronized(v) { if(!v.contains(o)) { v.add(o); } } } 这样不知道对不对

    作者回复: 对的

    2019-03-14
    4
    25
  • 易水南风
    add10k的例子不明白,因为两个方法都已经加上锁了,同一个test对象应该不可能两个线程同时执行吧?

    作者回复: 同时执行,指的是同时被调用。被锁串行后,还是有问题

    2019-03-15
    11
    23
  • hanmshashou
    ConcurrentHashMap 1.8后没有分段锁 syn + cas

    作者回复: 是这样,高手!

    2019-03-14
    2
    16
  • 探索无止境
    吞吐量和并发量从文中描述的概念上来看,总觉得很像,具体该怎么区分?期待指点!

    作者回复: 对于一台webserver,吞吐量一般指的是server每秒钟能处理多少请求;并发量指的是有多少个客户端同时访问。

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