• 并发编程大扫盲:带你了解何为线程上下文切换


    在并发程序中,关于线程数设置大小的说法:

    线程数量设置太小,可能会导致程序不能充分利用好系统资源。

    线程数量设置太大,可能会带来资源的过度竞争,导致上下文切换给系统带来额外的开销。

    何为上下文切换?
    在单核CPU时代,操作系统其实就已经能处理多线程并发任务了。处理器会给每个线程分配一个CPU时间片,线程会在获取的时间片内执行任务。

    时间片的概念是什么?
    时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。

    时间段一般为几十毫秒。

    如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。

    宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。当我们开启微信、QQ、IDEA、数据库等,我基本上是感受不到处理器不断的进行时间片切换。

    微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。

    何为上下文切换?
    当一个线程的时间片用完后或者其他自身原因被迫暂停运行了,这时候,另外一个线程或者、进程或者其他进程的线程就会白操作系统选中,用来占用处理器。

    这种一个线程被暂停,一个线程包选中开始执行的过程就叫做上下文切换。

    和CPU寄存器、程序计数器的关系
    上下文切换包含了寄存器的存储和程序计数器存储的指令内容。

    CPU寄存器负责存储已经、正在和即将要执行的任务。

    程序计数器负责寄存CPU正在执行的指令位置和即将执行的下一条指令的位置。

    上下文切换分类
    进程之间的上下文切换。
    线程之间的上下文切换(本文重点)。
    什么场景会导致线程的上下文切换?
    导致线程上下文切换的有两种类型:

    自发性上下文切换是指线程由 Java 程序调用导致切出,在多线程编程中,执行调用上图中的方法或关键字,常常就会引发自发性上下文切换。

    非自发性上下文切换指线程由于调度器的原因被迫切出。常见的有:线程被分配的时间片用完,虚拟机垃圾回收导致或者执行优先级的问题导致。

    那么虚拟机垃圾回收为什么会导致上下文切换 ?
    在 Java 虚拟机中,对象的内存都是由虚拟机中的堆分配的,在程序运行过程中,新的对象将不断被创建,如果旧的对象使用后不进行回收,堆内存将很快被耗尽。Java 虚拟机提供了一种回收机制,对创建后不再使用的对象进行回收,从而保证堆内存的可持续性分配。而这种垃圾回收机制的使用有可能会导致 stop-the-world 事件的发生,这其实就是一种线程暂停行为。

    怎么发现上下文切换?
    vmstat命令
    可以使用vmstat命令来查看线程上下文切换情况。

    参数说明
    procs

    r:等等运行的进程数。
    b:出在飞终端睡眠状态的进程数。
    memory

    swpd:虚拟内存使用情况,单位:KB。
    free :空闲的内存,单位:KB。
    buff:被用来作为缓存的内存数,单位:KB。
    swap

    si:每秒从交换区写到内存的大小 。
    so:每秒写入交换区的内存大小 。
    io

    bi: 每秒读取的块数
    bo: 每秒写入的块数
    system

    in: 每秒中断数,包括时钟中断。
    cs: 每秒上下文切换数。
    CPU

    us: 用户进程执行时间(user time)
    sy: 系统进程执行时间(system time)
    id: 空闲时间(包括IO等待时间),中央处理器的空闲时间 。以百分比表示。
    wa: 等待IO时间
    pidstat命令

    参数说明

    cswch/s(自愿):值进程无法获取所需资源导致的上下文切换,比如:I/O,内存等系统资源不足时,就会发生自愿上下文切换
    nvcswch/s(非自愿):值进程由于时间已到等原因,被系统强制调度而发生的上下文切换,比如,大量进程都在争夺CPU而发生非自愿上下文切换
    总结
    上下文切换就是一个工作的线程被另外一个线程暂停,另外一个线程占用了处理器开始执行任务的过程。系统和 Java 程序自发性以及非自发性的调用操作,就会导致上下文切换,从而带来系统开销。

    线程越多,系统的运行速度不一定越快。那么我们平时在并发量比较大的情况下,什么时候用单线程,什么时候用多线程呢?

    一般在单个逻辑比较简单,而且速度相对来非常快的情况下,我们可以使用单线程。例如,我们前面讲到的 Redis,从内存中快速读取值,不用考虑 I/O 瓶颈带来的阻塞问题。而在逻辑相对来说很复杂的场景,等待时间相对较长又或者是需要大量计算的场景,我建议使用多线程来提高系统的整体性能。例如,NIO 时期的文件读写操作、图像处理以及大数据分析等。

  • 相关阅读:
    c编写程序完成m名旅客和n辆汽车的同步程序代写
    [原]web服务器:SOAP,WSDL,UDDI
    用多进程同步方法演示“桔子-苹果”问题
    实验教学管理系统 c语言程序代写源码下载
    模拟游客一天的生活与旅游java程序代写源码
    Java作业代写
    快餐店运行模拟C++程序源码代写
    求可能组合VB源码代写
    深入源码分析Java线程池的实现原理
    ThreadLocal原理详解
  • 原文地址:https://www.cnblogs.com/tianweichang/p/14096604.html
Copyright © 2020-2023  润新知