简介

多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。

概括来说就是: 当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。

上下文切换的原因

多线程编程中,我们知道线程间的上下文切换会导致性能问题,那么是什么原因造成的线程间的上下文切换。我们先看一下线程的生命周期,从中看一下找找答案。

上下文切换

线程的五种状态我们都非常清楚:NEW、RUNNABLE、RUNNING、BLOCKED、DEAD,对应的Java中的六种状态分别为:NEW、RUNABLE、BLOCKED、WAINTING、TIMED_WAITING、TERMINADTED。

图中,一个线程从RUNNABLE到RUNNING的过程就是线程的上下文切换,RUNNING状态到BLOCKED、再到RUNNABLE、再从RUNNABLE到RUNNING的过程就是一个上下文切换的过程。 一个线程从RUNNING转为BLOCKED状态时,我们叫做线程的暂停,线程暂停了,这个处理器就会有别的线程来占用,操作系统就会保存相应的上下文,为了这个线程以后再进入RUNNABLE状态时可以接着之前的执行进度继续执行。当线程从BLOCKED状态进入到RUNNABLE时,也就是线程的唤醒,此时线程将获取上次保存的上下文信息。

我们看到,多线程的上下文切换实际上就是多线程两个运行状态的相互切换导致的。

我们知道两种情况可以导致上下文切换:

  1. 一种是程序本身触发的切换,这种我们一般称为自发性上下文切换。
  2. 另一种是系统或者虚拟机导致的上下文切换,我们称之为非自发性上下文切换。

评论