Java 性能调优实战
刘超
前金山软件技术经理
59174 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 49 讲
开篇词 (1讲)
模块一 · 概述 (2讲)
结束语 (1讲)
Java 性能调优实战
15
15
1.0x
00:00/00:00
登录|注册

16 | 多线程调优(下):如何优化多线程上下文切换?

锁分段
锁分离
优化wait/notify的使用,减少上下文切换
非阻塞乐观锁替代竞争锁
降低锁的粒度
减少锁的持有时间
减少Java虚拟机的垃圾回收
使用协程实现非阻塞等待
合理地设置线程池大小,避免创建过多线程
wait/notify优化
竞争锁优化
思考题
如何优化多线程上下文切换?

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

你好,我是刘超。
通过上一讲的讲解,相信你对上下文切换已经有了一定的了解了。如果是单个线程,在 CPU 调用之后,那么它基本上是不会被调度出去的。如果可运行的线程数远大于 CPU 数量,那么操作系统最终会将某个正在运行的线程调度出来,从而使其它线程能够使用 CPU ,这就会导致上下文切换。
还有,在多线程中如果使用了竞争锁,当线程由于等待竞争锁而被阻塞时,JVM 通常会将这个线程挂起,并允许它被交换出去。如果频繁地发生阻塞,CPU 密集型的程序就会发生更多的上下文切换。
那么问题来了,我们知道在某些场景下使用多线程是非常必要的,但多线程编程给系统带来了上下文切换,从而增加的性能开销也是实打实存在的。那么我们该如何优化多线程上下文切换呢?这就是我今天要和你分享的话题,我将重点介绍几种常见的优化方法。

竞争锁优化

大多数人在多线程编程中碰到性能问题,第一反应多是想到了锁。
多线程对锁资源的竞争会引起上下文切换,还有锁竞争导致的线程阻塞越多,上下文切换就越频繁,系统的性能开销也就越大。由此可见,在多线程编程中,锁其实不是性能开销的根源,竞争锁才是。
第 11~13 讲中我曾集中讲过锁优化,我们知道锁的优化归根结底就是减少竞争。这讲中我们就再来总结下锁优化的一些方式。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文介绍了如何优化多线程上下文切换的方法,主要包括竞争锁优化和wait/notify优化两个方面。在竞争锁优化方面,文章提到了减少锁的持有时间、降低锁的粒度和非阻塞乐观锁替代竞争锁等方法。在wait/notify优化方面,文章指出了使用Object.notify()替代Object.notifyAll()、尽快释放内部锁以及使用Lock锁结合Condition接口替代Synchronized内部锁中的wait/notify等方法。此外,文章还提到了合理设置线程池大小、使用协程实现非阻塞等待以及减少Java虚拟机的垃圾回收等优化方法。总的来说,本文通过介绍竞争锁优化和wait/notify优化的方法,为读者提供了在多线程编程中优化上下文切换的实用技巧,有助于提高系统的性能和并发能力。

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

全部留言(38)

  • 最新
  • 精选
  • Geek_1f1a07
    Zed说的不对,首先,所有的锁,无论synchronize还是lock,如果发生竞争条件,都可能造成上下文切换,优化锁的目的是为了尽量降低发生锁竞争的概率,synchronize做的优化都是把竞争的可能消灭在前期的偏向锁,轻量级锁,把会造成上下文切换的“脏活”留在最后。lock的乐观锁大体思路也是一样的,不到万不得已,不会轻易调用park方法。但是本质上java目前都是利用内核线程,所以都会有上下文切换。除非使用协程的技术,这个以前有green thread,后来不用了,期待老师后面对协程的讲解。

    作者回复: 回答很好,赞一个。

    2019-06-25
    57
  • 尔冬橙
    volitile的读写不会导致上下文切换,操作系统层面怎么理解呢

    作者回复: volatile主要是用来保证共享变量额可见性,以及防止指令重排序,保证执行的有序性。 通过生成.class文件之后,反编译文件我们可以看到通过volatile修饰的共享变量,在写入操作的时候会多一个Lock前缀这样的指令,当操作系统执行时会由于这个指令,将当前处理器缓存的数据写回系统内存中,并通知其他处理器中的缓存失效。 所以volatile不会带来线程的挂起操作,不会导致上下文切换。

    2019-09-21
    18
  • Zed
    回答趙衍同学 如你所说,synchronized主要是因为有用户态和内核态的交互所以能到进程级别。 而Lock是通过AQS的state状态来判断是否持有锁,整个过程都是在用户态或者说纯java实现。 最后lock.await()也是把当前线程放到当前条件变量的等待队列中并让出cpu。顺便提下,lock支持多条件变量。

    作者回复: 回答很好。线程进入阻塞,两者都会发生进程上下文切换。Synchronized中阻塞线程无论何时去获取锁,都需要进入到内核态,而AQS中,阻塞线程再次获取锁时,是通过state以及CAS操作判断,只有没有竞争成功时,才会再次被挂起,这样可以尽量减少上下文切换。

    2019-06-25
    15
  • QQ怪
    我觉得有些人建议使用notifyall的原因是使用notify需要有十足的把握去确认哪条线程需要唤醒,因为一不留神就容易搞错,为了优化而优化最后事倍功半,所以大家才会使用notifyall一劳永逸,我其实挺认同老师的观点,老师,全部唤醒会导致更多的上下文切换,是否要优化这点,我觉得还是得看个人了吧😂

    作者回复: notify()可以结合wait(long)方法使用,解决某些没有通知的线程被通知不到的问题

    2019-06-26
    3
    13
  • 你好旅行者
    老师好!在synchronized中,“挂起”这个动作是由JVM来实现的,获取不到锁的线程会被迫让出CPU,由于synchronized是基于操作系统的mutex机制,所以会产生进程的上下文切换。我想请问老师,在JDK的Lock中,或者AQS中,线程“挂起”这个动作又是怎么实现的呢?为什么不会产生进程级别的上下文切换呢?

    作者回复: AQS挂起是通过LockSupport中的park进入阻塞状态,这个过程也是存在进程上下文切换的。但被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成,这样就比AQS阻塞的线程更消耗系统资源了。

    2019-06-25
    2
    10
  • td901105
    老师,是不是使用Lock锁机制不会有用户态和内核态的切换?还是Lock本身锁机制是不涉及用户态到内核态的切换的,只是在未获取锁的时候需要使用内核态的方法比如park方法进行线程的挂起?

    作者回复: 一样会有,相对同步锁来说,只是减少了用户态和内核态的切换。Lock锁被阻塞的线程再次获取锁时,不会产生进程上下文切换,而synchronized阻塞的线程每次获取锁资源都要通过系统调用内核来完成。

    2019-12-20
    5
  • K
    老师好,我有个特别简单的小问题不太明白。既然用了vector,为什么还要用synchronize锁起来啊,vector本身不就是线程安全的?谢谢老师回答。

    作者回复: 这里的vector是一个对象锁,锁的是一个代码块,并不是保证vector的线程安全。

    2019-07-22
    2
    5
  • WL
    老师请问一下, JVM在操作系统层面是一个进程还是多个进程, 如果是一个进程的话, 那synchronize和park()方法发生的是进程级别的状态切换的话是指操作系统不运行JVM了吗?

    作者回复: 一个JVM在操作系统中只有一个进程,这里指的是进程中的某个运行的线程停止使用CPU,切换到内核获取CPU运行,而不是说停止JVM,然后运行内核。这里的切换是用户态使用CPU切换到了内核态使用CPU。

    2019-06-27
    5
  • 皮皮
    老师您好,一直有个疑问想请教,就是JDK1.5引入的lock锁底层实现也是调用了lockhelper的park和unpark方法,这个是否也涉及到系统的上下文切换,用户态和内核态的切换?

    作者回复: 是的

    2019-06-25
    4
  • 奇奇
    代码写错了 while(pool.isEmpty())不能放在同步代码块的外面 假设此时pool不为空容量为1,此时10个线程的pool.isEmpty都为false,此时全部跳出循环。 全部执行pool.remove(0) 错误

    编辑回复: 同学你好!后面有个锁,不会同时进去remove。如有疑问,可继续留言。

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