15 | 多线程调优(上):哪些操作导致了上下文切换?
15 | 多线程调优(上):哪些操作导致了上下文切换?
讲述:李良
时长10:01大小9.17M
初识上下文切换
多线程上下文切换诱因
发现上下文切换
总结
思考题
赞 17
提建议
精选留言(47)
- 晓杰2019-06-25锁的竞争太激烈会导致锁升级为重量级锁,未抢到锁的线程会进入monitor,而monitor依赖于底层操作系统的mutex lock,获取锁时会发生用户态和内核态之间的切换,所以会发生进程间的上下文切换。
作者回复: 对的
83 - 尔冬橙2019-09-20老师,网上有个争议,sleep会不会释放cpu时间片?
作者回复: sleep不会释放锁资源,wait则会,但两者都会从running状态中走出,也就是不再占用CPU资源。
35 - 李博2019-06-22如果Synchronized块中包含io操作或者大量的内存分配时,可能会导致进程IO等待或者内存不足。进一步会导致操作系统进行进程切换,等待系统资源满足时在切换到当前进程。 不知道理解的对不对?
作者回复: 进程上下文切换,是指用户态和内核态的来回切换。我们知道,如果一旦Synchronized锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取mutex,这个过程就是进程上下文切换。
共 3 条评论28 - 内卷改变命运2019-11-25进程的上下文切换是指从一个进程切换到另一个进程运行。用户态到内核态为什么也属于进程上下文切换?从哪个进程切换到哪个进程了?
作者回复: 在linux操作系统中,进程的运行空间一般分为用户态和内核态,用户态空间一般是进程应用运行空间,而内核态空间一般是指应用需要调用系统资源,应用不能再用户态空间直接调用系统资源,需要通过内核态来系统系统资源。 所以进程在用户态和内核态两个直接相互切换,就称之为进程上下文切换。
17 - 钱2019-09-09课后思考及问题 本文核心观点 1:线程上下文切换指啥? 线程上下文切换指一个线程被暂停剥夺对CPU的使用权,另外一个线程被选中开始或者继续在CPU中运行的过程。 2:线程上文切换的问题? 上下文切换会导致额外的性能开销,因为一个线程正在CPU上执行需要停下来换另外一个线程来执行,需要做许多的事情。 3:上下文切换的性能开销花费在哪里啦? 操作系统保存和恢复上下文; 调度器进行线程调度; 处理器高速缓存重新加载; 上下文切换也可能导致整个高速缓存区被冲刷,从而带来时间开销。 4:上下文切换的分类? 上下文切换分为进程上下文切换和线程上下文切换,这是站在操作系统的层面来讲的,站在CPU的角度,它不知道什么进程、线程之类的东西,他只需要知道怎么取指令怎么执行就行啦! 5:上下文切换的发送场景? 上下文切换分为两种,一是自发上下文切换,另一个是非自己上下文切换。 6:啥是自发上下文切换?怎么触发? 自发性上下文切换指线程由 Java 程序调用导致切出,在多线程编程中,执行调用以下方法或关键字,常常就会引发自发性上下文切换。 sleep() wait() yield() join() park() synchronized lock 7:啥是非自发上下文切换?怎么触发? 非自发性上下文切换指线程由于调度器的原因被迫切出。常见的有:线程被分配的时间片用完,虚拟机垃圾回收导致或者执行优先级的问题导致。 8:在多线程中使用 Synchronized 会发生进程间的上下文切换,具体的发生环节如下(声明:答案我没想到,参考评论区老师的回答)? 当升级到重量级锁后,线程竞争锁资源,将会进入等待队列中,并在等待队列中不断尝试获取锁资源。每次去获取锁资源,都需要通过系统调底层操作系统申请获取Mutex Lock,这个过程就是一次用户态和内核态的切换。 有个疑问,老师具体没讲什么是进程的上下文切换?什么是线程的上下文切换?评论区中又特意强调进程间的上下文切换指用户态和内核态之间的切换。那线程间的切换指什么?线程一直在内核态空间中,只是切出了CPU是吗?展开
作者回复: 进程间的上下文切换因为是用户态和内核态之间的切换,需要消耗更多的资源,例如,寄存器中的内容切换出,缓存的刷新等,而线程间的上下文切换是用户态的线程切换,由于是同一个虚拟内存,消耗资源相对较少。
12 - 老杨同志2019-06-22使用Synchronized获得锁失败,进入等待队列会发生上下文切换。如果竞争锁时锁是其他线程的偏向锁,需要降级,这是需要stop the world也会发生上下文切换
作者回复: 理解正确~
共 3 条评论10 - 尔冬橙2019-09-20老师,可以讲下进程从用户态到内核态为什么就发生了上下问切换呢
作者回复: Java应用进程里面执行的代码则是属于用户态,而系统内核代码执行则属于内核态,假设我们一个同步锁的执行,首先会在Java应用程序中执行,之后需要切换到系统内核中去获取mutex,在切换的时候,需要将原来用户态的执行指令与内核执行指令相互切换,此时CPU资源也会发生切出切入。
8 - Liam2019-06-22老师好,请教一个问题: 1 非自发场景中,cpu time slice 用完后切换线程,此时被暂停线程是什么状态呢?runnable or blocked, 如果是runnable的话,是不是意味着从runnning到runnable也会导致上下文切换
作者回复: 我们可以调用yield,线程可以从runnning到runnable,我们可以手动编程试试,看看cs是不是增加了。 yield也会导致上下文切换的。
6 - 吴青2019-11-24老师,你是不是把waiting和timed_waiting状态漏了
作者回复: 这里指的是系统中线程的五个基本状态,waiting以及timed_waiting属于阻塞,这里的阻塞包括争用锁导致的阻塞blocked状态、调用wait(timeout)导致的timed_waiting状态以及调用wait()导致的waiting状态。
5 - 欧星星2019-06-22使用Synchronized在锁获取的时候会发生CPU上下文切换,多线程本身也会有上下文切换,这样就会多一次切换,是这样吗?
作者回复: Synchronized在轻量级锁之前,锁资源竞争产生的是线程上下文切换,一旦升级到重量级锁,就会产生进程上下文切换。
5 - -W.LI-2019-06-23老师好!看了大牛们的课后习题回答,大概意思就是偏斜锁,轻量级锁这种不涉及进程切换。然后并发严重膨胀为重量级锁了,发生blocked了或者调用wait(),join()方法释放锁资源,就会触发进程切换了。CAS这种乐观锁,不会触发进程上下文切换?LOCK呢?在调用pack()的时候会导致进程切换么?lock()方法直接获取到锁,没有发生寻找安全点的时候是不是就不会触发进程上下文切换了? 纯属瞎猜,希望老师解惑谢谢。展开
作者回复: CAS乐观锁只是一个原子操作,为CPU指令实现,所以操作速度非常快,Java是调用C语言中的函数执行CPU指令操作,不需要进入内核或者切换线程。 而lock竞争锁资源是基于用户态完成,所以竞争锁资源时不会发生进程上下文切换。
4 - QQ怪2019-06-22老师讲的上下文切换的确干货很多,思考题我觉得应该是使用synchronize导致单线程进行,且执行方法时间过长,当前进程时间片执行时间结束,导致cpu不得不进行进程间上下文切换。
作者回复: 进程上下文切换,是指用户态和内核态的来回切换。当Synchronized锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取mutex,这个过程是Synchronized锁产生的进程上下文切换。
4 - 悟空WuKong2019-10-24请问 线程池中 一个线程处理完一个任务之后会发生上下文切换吗?如果接下来的任务还是该线程处理,线程是不是一直在运行状态,是不是就不会发生上下文切换?
作者回复: 对的,协程就是一个典型的例子,一个线程中通过任务调度执行不同的任务,没有发生线程间的上下文切换。
共 2 条评论3 - LW2019-06-24看回复老师说锁升级到重量级锁,就会发生进程间切换,这个点能详细讲讲吗?
作者回复: 当升级到重量级锁后,线程竞争锁资源,将会进入等待队列中,并在等待队列中不断尝试获取锁资源。每次去获取锁资源,都需要通过系统调底层操作系统申请获取Mutex Lock,这个过程就是一次用户态和内核态的切换。
3 - ヾ(◍°∇°◍)ノ゙2019-06-22sleep引起上下文切换是指系统调用吗?用户态到内核态的切换。但是这时候线程会从running变成block吗?感觉这个线程没有让出控制吧,跟wait不一样的吧
作者回复: sleep和wait一样,都会进入阻塞状态,区别是sleep没有释放锁,而wait释放了锁。所以也是一次上下文切换。
3 - Bumblebee2022-05-13今日收获 ① 在并发程序中并不是启用更多的线程就能让程序最大限度的并发执行; ② 线程从Running 变为其他状态就发生一次线程上下文的切换; ③ 线程从Running 变为blocked我们称一个线程暂停,此时操作系统会保存该线程此时的上下文,以便线程再次进入Running 继续执行; ④ 线程上下文切换分自发性的(调用sleep、wait、yield、join、park、synchronized、lock)与非自发性的(比如线程分配的cpu时间片到了、虚拟机回收垃圾发生stop the world等); ⑤ 进程上下文切换(用户态与内核态的转换)synchronized升级为重量级锁时,会触发进程上下文切换; ⑥ 单线程(逻辑简单,处理速度较快)与多线程(逻辑复杂,等待时间长或是大量计算(注意做好线程池隔离))的使用场景; ⑦ 监控上下文切换的工具(vmstat 命令,来监视 Java 程序运行过程中系统的上下文切换频率,cs )、(如果是监视某个应用的上下文切换,就可以使用 pidstat 命令监控指定进程的 Context Switch 上下文切换。) ⑧ 上下文切换系统开销具体发生在切换过程中的哪些具体环节,总结如下:(操作系统保存和恢复上下文、调度器进行线程调度、处理器高速缓存重新加载、上下文切换也可能导致整个高速缓存区被冲刷,从而带来时间开销); 总结 ① 避免jvm频繁的full gc 发生stop the world导致线程发生上下文切换; ② 对于竞争激烈的同步代码少用synchronized,因为竞争激励容易使synchronized升级到重量级锁从而导致进程上下文切换(用户态与内核态的转换); ③ 避免线程池线程过多导致竞争激烈(频繁上下文切换)展开2
- 汤小高2019-06-22老师能否提供一份全面的如何定位性能方面问题的工具或者命令了,比如操作系统层面的,也就是文章中提到的,或者JAVA工具层面的。能出一篇这种通过相关命令或者工具定位排查问题的案例最好不过了。
作者回复: 好的,后面我总结一份命令排查工具的使用报告给大家。
3 - Jxin2019-06-22首先,如何决定多线程。这点核心的依据我认为是提高计算机资源使用率。将cpu执行耗时较长比如io操作,跟耗时较短比如纯逻辑计算的业务操作分解开。根据时间比例对应分配操作线程池的线程数。进而保障资源最大化利用,比如耗时较短的业务线程不会空闲。理论上多核的现在,并行逻辑都要比串行逻辑快(并行交集时间,既剩下的时间大于上下文切换和资源合并的时间开销)。其实我觉得还得引入业务价值做考虑,核心业务加大优化力度,边缘业务性能保持在容忍线以上就好,为核心业务让渡资源。最后是思考题。才疏学浅,隐式锁个人认为是为线程价格执行体准备的,不会影响到进程间的切换。但是多进程间用的也是同一台服务器的资源。所以必然也会有上下文切换,而这块都是非自发的。比如cpu时间分片呈现的进程间交替使用cpu,或则进程各自持有的虚拟内存页对实际物理内存的使用。至于文件操作,java发现文件资源被其他进程占用好像是直接报错的,所以没有进程间竞争。但输出设备,打印机音箱这些,它们有多进程轮流共用的现象,感觉起来也有点分片执行,优先调度之类的样子,应该也有竞争。个人认知半猜回复,还望老师指正。搬砖引玉。展开2
- -W.LI-2019-06-22老师好!我看Java源码里面线程状态是 1.new 2.runnable 3.wait 4.timed-wait 5.blocked 6.i..(完成)。 有次面试被问了这个,我一开始答就是网上说的new,runnable,running,wait,blocked,dead。然后面试官让我回去再看看。到底线程状态有几个,怎么划分啊。 大概一个分片多少时间比较合理啊,单核CPU设置多少线程比较合适?之前有个调用第三方的一次得发送4K请求,测试下来开50个线程的时候效果比较好16S完成。这个线程数和网上说的CPU核心数*(IO阻塞时间/计算时间)出路挺大的。服务器我们用的单核服务器,Tomcat连接池,MySQL连接池这些都加上我感觉线程数很可能过100了。能讲讲具体调试的步骤和指标么。实战优化过程经验谢谢老师。展开
作者回复: 这五个状态是指的系统的线程状态。如果说Java线程状态,我们可以分为new、runable、waiting、temed-wait、blocked、terminated。 这个分片时间我们无法调节,我们需要的是避免竞争过于激烈导致过多的上下文切换。线程池的线程调优这块,我们后面会有一讲专门讲到。
共 2 条评论2 - 尔冬橙2019-09-20老师,按道理任务多时大部分情况下并发处理不应该比单线程快么?redis使用单线程不是因为串行快吧。
作者回复: 如果执行任务耗时,多线程可以提高执行效率。Redis之所以快,是因为redis主要是操作内存,没有磁盘IO操作,读写都非常快,其次串行减少上下文切换带来的时间开销。
1