【多线程性能调优】多线程调优(上):哪些操作导致了上下文切换?

  在并发程序中,并不是启动更多的线程就能让程序最大限度地并发执行。线程数量设置太小,会导致程序不能充分地利用系统资源;线程数量设置太大,又可能带来资源的过度竞争,导致上下文切换带来额外的系统开销。

什么是上下文切换

  时间片决定了一个线程可以连续占用处理器运行的时长。当一个线程的时间片用完了,或者因自身原因被迫暂停运行了,这个时候,另外一个线程(可以是同一个线程或者其它进程的线程)就会被操作系统选中,来占用处理器。这种一个线程被暂停剥夺使用权,另外一个线程被选中开始或者继续运行的过程就叫做上下文切换(Context Switch)。
  上下文都包括哪些内容呢?包括了寄存器的存储内容以及程序计数器存储的指令内容。

多线程上下文切换诱因

  在多线程编程中,我们主要面对的是线程间的上下文切换导致的性能问题。

在这里插入图片描述
  自发性上下文切换指线程由 Java 程序调用导致切出,在多线程编程中,执行调用以下方法或关键字,常常就会引发自发性上下文切换。

  • sleep()
  • wait()
  • yield()
  • join()
  • park()
  • synchronized
  • lock

  非自发性上下文切换指线程由于调度器的原因被迫切出。常见的有:线程被分配的时间片用完,虚拟机垃圾回收导致或者执行优先级的问题导致。
  虚拟机垃圾回收为什么会导致上下文切换?虚拟机垃圾回收机制的使用有可能会导致 stop-the-world 事件的发生,这其实就是一种线程暂停行为

发现上下文切换

  串联的执行速度比并发的执行速度要快,这就是因为线程的上下文切换导致了额外的开销。系统开销具体发生在切换过程中的哪些具体环节,总结如下:

  • 操作系统保存和恢复上下文;
  • 调度器进行线程调度;
  • 处理器高速缓存重新加载;
  • 上下文切换也可能导致整个高速缓存区被冲刷,从而带来时间开销。

什么时候用多线程?

  一般在单个逻辑比较简单,而且速度相对非常快的情况下,我们可以使用单线程。例如, Redis从内存中快速读取值,不用考虑 I/O 瓶颈带来的阻塞问题。而在逻辑相对来说很复杂的场景,等待时间相对较长又或者是需要大量计算的场景,建议使用多线程来提高系统的整体性能。例如,NIO 的文件读写操作、图像处理以及大数据分析等。
  在多线程中使用 Synchronized 还会发生进程间的上下文切换,具体会发生在哪些环节呢?进程上下文切换,是指用户态和内核态的来回切换。如果一旦 Synchronized 锁资源竞争激烈,线程将会被阻塞,阻塞的线程将会从用户态调用内核态,尝试获取 mutex,这个过程就是进程上下文切换。

猜你喜欢

转载自blog.csdn.net/ChinaLiaoTian/article/details/123171873
今日推荐