多线程的上下文切换-影响-如何优化

线程的上下文切换

上下文切换

1.对于CPU而言,在一个时刻只能运因自身原因被迫暂停运行,此时另一个线程会被操作系统选中来占用处理器行一个线程,当一个线程的时间片用完,或者

2.当CPU结束运行一个线程,转去执行另外一个线程,这个过程就叫做线程上下文切换

上下文

1.在发生切换的时候,当前线程的任务可能并没有执行完毕。所以在切换时需要保存线程切换前的运行状态,以便下一次,可以接着切换之前的状态继续执行后续的任务

2.切出切入的过程中,操作系统需要保存和恢复相应的进度信息,这个进度信息就是上下文

比如一个线程A正在读取一个文件的内容,正读到文件的一半,线程A的时间片结束,此时需要暂停线程A,CPU转去执行线程;当再次切换回来执行线程A的时候,我们不希望线程A又从文件的开头来读取

 

上下文的内容

线程上下文切换过程中会涉及程序计数器(PCB,位于内存之中)、CPU寄存器∶

  1. 寄存器的存储内容∶CPU寄存器负责存储已经、正在和将要执行的任务
  2. 程序计数器存储的指令内容∶程序计数器负责存储CPU正在执行的指令位置、即将执行的下一条指令的位置

CPU寄存器

在线程正在进行某个计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起时变量的值时多少(也就是直接读取中间的结果,而不是从头开始读);中间结果会放在CPU的寄存器中

程序计数器

  1. 线程在进行切换时候,需要知道在这之前当前线程已经执行到哪条指令了,这些指令信息需要依靠程序计数器来保存
  2. 程序计数器是一块较小的内存空间,它保存了当前线程下一条需要执行的字节码指令的位置
  3. 每条线程都需要有一个独立的程序计数器,线程之间的程序计数器互不影响,寄存器和CPU是不同的

带来的系统开销

Java的线程是映射到操作系统原生线程之上的,如果要阻塞唤醒一个线程就需要操作系统介入,需要在用户态与内核态之间切换,这种切换会消耗大量的系统资源;比如运行的QQ和微信等都是运行在用户态中的

用户态-内核态

  • 在执行用户自己的代码时,称其处于用户态此时处理器特权级最低,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。(比如QQ,微信等等)
  • 当因为系统调用陷入内核代码中执行时(阻塞和唤醒),处于内核态,此时处理器处于特权级最高
  • 如果要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,此时需要从用户态切换到内核态。这些系统调用会调用内核的代码,在执行完后又会切换回用户态
  • 用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值变量等,以便内核态调用结束后切换回用户态继续工作。

 

上下文切换的两种方式:自发性上下文切换和非自发性地上下文切换

自发性上下文切换:主动进入阻塞状态

  • sleep  
  • wait ;等待一些情形的发生和等待锁
  • yield :主动地释放自己的CPU资源
  • join等待其他线程完成,然后再继续自己的任务
  • park
  • synchronized  
  • lock

非自发性上下文切换:被动进入阻塞状态

  • 线程被分配的时间片用完
  • JVM垃圾回收(STW Stop the world:full GC、线程暂停)
  • 线程执行优先级
  • 如何优化?-从减少线程上下文切换的角度出发

减少锁的竞争

  1.  多线程对锁资源的竞争,如果失败由于进入阻塞状态,将会引起上下文切换(锁本身不是带来性能开销的本质原因,锁竞争才是)
  2. 锁竞争导致的线程阻塞越多,上下文切换就越频繁,系统的性能开销就越大
  3. 在多线程编程中,锁本身不是性能开销的根源,锁竞争才是性能开销的根源
  4.  锁优化归根到底是减少竞争
  • 减少锁的持有时间

  • 锁的持有时间越长,意味着越多的线程会被阻塞,在等待该竞争锁释放
  • 优化方法∶将一些与锁无关的代码移出同步代码块,尤其是那些开销较大的操作以及可能被阻塞的操作
  • 减少锁的粒度-锁分离

  • 读写锁实现了锁分离,由读锁和写锁两个锁实现,可以共享读,但只有一个写
  • 读写锁在多线程读写时,读读不互斥,读写互斥,写写互斥
  • 传统的独占锁在多线程读写时,读读互斥,读写互斥,写写互斥
  • 在读远大于写的多线程场景中,锁分离避免了高并发读情况下的资源竞争,从而避免了上下文切换
  • 减少锁的粒度-锁分段

  • 在使用锁来保证集合或者大对象的原子性时,可以将锁对象进一步分解
  • Java 1.8之前的ConcurrentHashMap就是用了锁分段
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多线程中的上下文切换是指在多个线程之间切换执行的过程。当一个线程正在执行时,如果有其他线程需要执行,操作系统暂停当前线程的执行,并保存其当前的上下文(包括程序计数器、寄存器值、栈指针等信息),然后切换到下一个线程的上下文,使其开始执行。这个过程就是上下文切换上下文切换是操作系统进行调度的基本机制之一,它可以实现多个线程并发执行,提高系统的资源利用率。然而,上下文切换带来一定的开销,因为保存和恢复上下文需要消耗额外的时间和资源。 上下文切换的开销主要包括以下几个方面: 1. 保存和恢复寄存器状态:因为每个线程都有自己的寄存器状态,所以在上下文切换时需要保存当前线程的寄存器状态,并恢复下一个线程的寄存器状态。 2. 切换内核栈:每个线程都有自己的内核栈,用于保存临时变量和函数调用信息。在上下文切换时,需要切换内核栈。 3. 更新页表:当线程切换时,可能涉及到虚拟内存的映射关系变化,需要更新页表来确保正确的地址访问。 4. 刷新硬件状态:上下文切换还可能需要刷新一些硬件状态,如缓存和TLB等。 上下文切换的频率过高导致系统性能下降,因此在编写多线程程序时,需要合理设计线程的数量和调度策略,以尽量减少上下文切换的次数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值