Java性能调优实战
刘超
金山软件西山居技术经理
立即订阅
7535 人已学习
课程目录
已完结 48 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 怎样才能做好性能调优?
免费
模块一 · 概述 (2讲)
01 | 如何制定性能调优标准?
02 | 如何制定性能调优策略?
模块二 · Java编程性能调优 (10讲)
03 | 字符串性能优化不容小觑,百M内存轻松存储几十G数据
04 | 慎重使用正则表达式
05 | ArrayList还是LinkedList?使用不当性能差千倍
加餐 | 推荐几款常用的性能测试工具
06 | Stream如何提高遍历集合效率?
07 | 深入浅出HashMap的设计与优化
08 | 网络通信优化之I/O模型:如何解决高并发下I/O瓶颈?
09 | 网络通信优化之序列化:避免使用Java序列化
10 | 网络通信优化之通信协议:如何优化RPC网络通信?
11 | 答疑课堂:深入了解NIO的优化实现原理
模块三 · 多线程性能调优 (10讲)
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
15 | 多线程调优(上):哪些操作导致了上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换?
17 | 并发容器的使用:识别不同场景下最优容器
18 | 如何设置线程池大小?
19 | 如何用协程来优化多线程业务?
20 | 答疑课堂:模块三热点问题解答
加餐 | 什么是数据的强、弱一致性?
模块四 · JVM性能监测及调优 (6讲)
21 | 磨刀不误砍柴工:欲知JVM调优先了解JVM内存模型
22 | 深入JVM即时编译器JIT,优化Java编译
23 | 如何优化垃圾回收机制?
24 | 如何优化JVM内存分配?
25 | 内存持续上升,我该如何排查问题?
26 | 答疑课堂:模块四热点问题解答
模块五 · 设计模式调优 (6讲)
27 | 单例模式:如何创建单一对象优化系统性能?
28 | 原型模式与享元模式:提升系统性能的利器
29 | 如何使用设计模式优化并发编程?
30 | 生产者消费者模式:电商库存设计优化
31 | 装饰器模式:如何优化电商系统中复杂的商品价格策略?
32 | 答疑课堂:模块五思考题集锦
模块六 · 数据库性能调优 (8讲)
33 | MySQL调优之SQL语句:如何写出高性能SQL语句?
34 | MySQL调优之事务:高并发场景下的数据库事务调优
35 | MySQL调优之索引:索引的失效与优化
36 | 记一次线上SQL死锁事故:如何避免死锁?
37 | 什么时候需要分表分库?
38 | 电商系统表设计优化案例分析
39 | 数据库参数设置优化,失之毫厘差之千里
40 | 答疑课堂:MySQL中InnoDB的知识点串讲
模块七 · 实战演练场 (4讲)
41 | 如何设计更优的分布式锁?
42 | 电商系统的分布式事务调优
43 | 如何使用缓存优化系统性能?
44 | 记一次双十一抢购性能瓶颈调优
结束语 (1讲)
结束语 | 栉风沐雨,砥砺前行!
Java性能调优实战
登录|注册

15 | 多线程调优(上):哪些操作导致了上下文切换?

刘超 2019-06-22
你好,我是刘超。
我们常说“实践是检验真理的唯一标准”,这句话不光在社会发展中可行,在技术学习中也同样适用。
记得我刚入职上家公司的时候,恰好赶上了一次抢购活动。这是系统重构上线后经历的第一次高并发考验,如期出现了大量超时报警,不过比我预料的要好一点,起码没有挂掉重启。
通过工具分析,我发现 cs(上下文切换每秒次数)指标已经接近了 60w ,平时的话最高 5w。再通过日志分析,我发现了大量带有 wait() 的 Exception,由此初步怀疑是大量线程处理不及时导致的,进一步锁定问题是连接池大小设置不合理。后来我就模拟了生产环境配置,对连接数压测进行调节,降低最大线程数,最后系统的性能就上去了。
从实践中总结经验,我知道了在并发程序中,并不是启动更多的线程就能让程序最大限度地并发执行。线程数量设置太小,会导致程序不能充分地利用系统资源;线程数量设置太大,又可能带来资源的过度竞争,导致上下文切换带来额外的系统开销。
你看,其实很多经验就是这么一点点积累的。那么今天,我就想和你分享下“上下文切换”的相关内容,希望也能让你有所收获。

初识上下文切换

我们首先得明白,上下文切换到底是什么。
其实在单个处理器的时期,操作系统就能处理多线程并发任务。处理器给每个线程分配 CPU 时间片(Time Slice),线程在分配获得的时间片内执行任务。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java性能调优实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(36)

  • 晓杰
    锁的竞争太激烈会导致锁升级为重量级锁,未抢到锁的线程会进入monitor,而monitor依赖于底层操作系统的mutex lock,获取锁时会发生用户态和内核态之间的切换,所以会发生进程间的上下文切换。

    作者回复: 对的

    2019-06-25
    20
  • 李博
    如果Synchronized块中包含io操作或者大量的内存分配时,可能会导致进程IO等待或者内存不足。进一步会导致操作系统进行进程切换,等待系统资源满足时在切换到当前进程。 不知道理解的对不对?

    作者回复: 进程上下文切换,是指用户态和内核态的来回切换。我们知道,如果一旦Synchronized锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取mutex,这个过程就是进程上下文切换。

    2019-06-22
    8
  • 老杨同志
    使用Synchronized获得锁失败,进入等待队列会发生上下文切换。如果竞争锁时锁是其他线程的偏向锁,需要降级,这是需要stop the world也会发生上下文切换

    作者回复: 理解正确~

    2019-06-22
    5
  • QQ怪
    老师讲的上下文切换的确干货很多,思考题我觉得应该是使用synchronize导致单线程进行,且执行方法时间过长,当前进程时间片执行时间结束,导致cpu不得不进行进程间上下文切换。

    作者回复: 进程上下文切换,是指用户态和内核态的来回切换。当Synchronized锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取mutex,这个过程是Synchronized锁产生的进程上下文切换。

    2019-06-22
    3
  • Jxin
    首先,如何决定多线程。这点核心的依据我认为是提高计算机资源使用率。将cpu执行耗时较长比如io操作,跟耗时较短比如纯逻辑计算的业务操作分解开。根据时间比例对应分配操作线程池的线程数。进而保障资源最大化利用,比如耗时较短的业务线程不会空闲。理论上多核的现在,并行逻辑都要比串行逻辑快(并行交集时间,既剩下的时间大于上下文切换和资源合并的时间开销)。其实我觉得还得引入业务价值做考虑,核心业务加大优化力度,边缘业务性能保持在容忍线以上就好,为核心业务让渡资源。最后是思考题。才疏学浅,隐式锁个人认为是为线程价格执行体准备的,不会影响到进程间的切换。但是多进程间用的也是同一台服务器的资源。所以必然也会有上下文切换,而这块都是非自发的。比如cpu时间分片呈现的进程间交替使用cpu,或则进程各自持有的虚拟内存页对实际物理内存的使用。至于文件操作,java发现文件资源被其他进程占用好像是直接报错的,所以没有进程间竞争。但输出设备,打印机音箱这些,它们有多进程轮流共用的现象,感觉起来也有点分片执行,优先调度之类的样子,应该也有竞争。个人认知半猜回复,还望老师指正。搬砖引玉。
    2019-06-22
    2
  • 水货
    进程的上下文切换是指从一个进程切换到另一个进程运行。用户态到内核态为什么也属于进程上下文切换?从哪个进程切换到哪个进程了?

    作者回复: 在linux操作系统中,进程的运行空间一般分为用户态和内核态,用户态空间一般是进程应用运行空间,而内核态空间一般是指应用需要调用系统资源,应用不能再用户态空间直接调用系统资源,需要通过内核态来系统系统资源。

    所以进程在用户态和内核态两个直接相互切换,就称之为进程上下文切换。

    2019-11-25
    1
  • 王宝
    请问 线程池中 一个线程处理完一个任务之后会发生上下文切换吗?如果接下来的任务还是该线程处理,线程是不是一直在运行状态,是不是就不会发生上下文切换?

    作者回复: 对的,协程就是一个典型的例子,一个线程中通过任务调度执行不同的任务,没有发生线程间的上下文切换。

    2019-10-24
    1
  • 勿闻轩外香
    干货满满😌
    2019-06-24
    1
  • 欧星星
    使用Synchronized在锁获取的时候会发生CPU上下文切换,多线程本身也会有上下文切换,这样就会多一次切换,是这样吗?

    作者回复: Synchronized在轻量级锁之前,锁资源竞争产生的是线程上下文切换,一旦升级到重量级锁,就会产生进程上下文切换。

    2019-06-22
    1
  • 💪😊
    sleep引起上下文切换是指系统调用吗?用户态到内核态的切换。但是这时候线程会从running变成block吗?感觉这个线程没有让出控制吧,跟wait不一样的吧

    作者回复: sleep和wait一样,都会进入阻塞状态,区别是sleep没有释放锁,而wait释放了锁。所以也是一次上下文切换。

    2019-06-22
    1
  • 木木匠
    思考题:多线程会导致线程间的上下文切换,而使用同步锁会导致多线程之间串行化,会增加程序执行时间,而过长的执行时间可能导致分配给本进程的时间片不够用,从而发生进程间的上下文切换。
    2019-06-22
    1
  • 吴青
    老师,你是不是把waiting和timed_waiting状态漏了

    作者回复: 这里指的是系统中线程的五个基本状态,waiting以及timed_waiting属于阻塞,这里的阻塞包括争用锁导致的阻塞blocked状态、调用wait(timeout)导致的timed_waiting状态以及调用wait()导致的waiting状态。

    2019-11-24
  • yintianwen
    请问老师,用户态到内核态的切换,都属于进程间的上下文切换吗?

    作者回复: 是的

    2019-10-17
  • 疯狂咸鱼
    老师,线程上下文切换是如何影响缓存的?不是很理解

    作者回复: 不会影响缓存,影响到的是系统的性能

    2019-09-21
  • 疯狂咸鱼
    老师,按道理任务多时大部分情况下并发处理不应该比单线程快么?redis使用单线程不是因为串行快吧。

    作者回复: 如果执行任务耗时,多线程可以提高执行效率。Redis之所以快,是因为redis主要是操作内存,没有磁盘IO操作,读写都非常快,其次串行减少上下文切换带来的时间开销。

    2019-09-20
  • 疯狂咸鱼
    老师,网上有个争议,sleep会不会释放cpu时间片?

    作者回复: sleep不会释放锁资源,wait则会,但两者都会从running状态中走出,也就是不再占用CPU资源。

    2019-09-20
  • 疯狂咸鱼
    老师,可以讲下进程从用户态到内核态为什么就发生了上下问切换呢

    作者回复: Java应用进程里面执行的代码则是属于用户态,而系统内核代码执行则属于内核态,假设我们一个同步锁的执行,首先会在Java应用程序中执行,之后需要切换到系统内核中去获取mutex,在切换的时候,需要将原来用户态的执行指令与内核执行指令相互切换,此时CPU资源也会发生切出切入。

    2019-09-20
  • godtrue
    课后思考及问题
    本文核心观点
    1:线程上下文切换指啥?
    线程上下文切换指一个线程被暂停剥夺对CPU的使用权,另外一个线程被选中开始或者继续在CPU中运行的过程。
    2:线程上文切换的问题?
    上下文切换会导致额外的性能开销,因为一个线程正在CPU上执行需要停下来换另外一个线程来执行,需要做许多的事情。
    3:上下文切换的性能开销花费在哪里啦?
    操作系统保存和恢复上下文;
    调度器进行线程调度;
    处理器高速缓存重新加载;
    上下文切换也可能导致整个高速缓存区被冲刷,从而带来时间开销。
    4:上下文切换的分类?
    上下文切换分为进程上下文切换和线程上下文切换,这是站在操作系统的层面来讲的,站在CPU的角度,它不知道什么进程、线程之类的东西,他只需要知道怎么取指令怎么执行就行啦!
    5:上下文切换的发送场景?
    上下文切换分为两种,一是自发上下文切换,另一个是非自己上下文切换。
    6:啥是自发上下文切换?怎么触发?
    自发性上下文切换指线程由 Java 程序调用导致切出,在多线程编程中,执行调用以下方法或关键字,常常就会引发自发性上下文切换。
    sleep()
    wait()
    yield()
    join()
    park()
    synchronized
    lock
    7:啥是非自发上下文切换?怎么触发?
    非自发性上下文切换指线程由于调度器的原因被迫切出。常见的有:线程被分配的时间片用完,虚拟机垃圾回收导致或者执行优先级的问题导致。
    8:在多线程中使用 Synchronized 会发生进程间的上下文切换,具体的发生环节如下(声明:答案我没想到,参考评论区老师的回答)?
    当升级到重量级锁后,线程竞争锁资源,将会进入等待队列中,并在等待队列中不断尝试获取锁资源。每次去获取锁资源,都需要通过系统调底层操作系统申请获取Mutex Lock,这个过程就是一次用户态和内核态的切换。
    有个疑问,老师具体没讲什么是进程的上下文切换?什么是线程的上下文切换?评论区中又特意强调进程间的上下文切换指用户态和内核态之间的切换。那线程间的切换指什么?线程一直在内核态空间中,只是切出了CPU是吗?

    作者回复: 进程间的上下文切换因为是用户态和内核态之间的切换,需要消耗更多的资源,例如,寄存器中的内容切换出,缓存的刷新等,而线程间的上下文切换是用户态的线程切换,由于是同一个虚拟内存,消耗资源相对较少。

    2019-09-09
  • K
    老师好,我看了下面老师对于进程上下文切换的解答,“当Synchronized锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取mutex,这个过程是Synchronized锁产生的进程上下文切换。”有点疑问就是:1.为什么阻塞的线程会从用户态调用内核态呢?想不到原因和这个过程。2.获取mutex就是进程上下文切换了吗?还是没明白为什么获取mutex就算进程切换了。谢谢老师帮忙答疑。

    作者回复: 因为获取mutex是内核来完成的,这里的进程上下文切换是指用户态和内核态的切换。

    2019-07-21
  • 张德
    老师讲一下 counter在多线程的情况下会不会超过一亿次呢?? 是如何保证线程安全的

    作者回复: synchronized (this)+if()判断保证了counter不会超过一个亿

    2019-07-11
收起评论
36
返回
顶部