基础概念
进程和线程
进程
我们通常所说的应用程序(即 app)由指令和数据组成。但是在没有运行具体 app 的情况下,这些应用程序就是一些二进制代码存储在磁盘(包括 U 盘、远程网络存储等)上。当我们运行这些应用程序时,指令加载到 CPU 中运行,数据则被加载到内存中读写。在指令运行过程中,还需要使用磁盘、网络等设备,进程负责加载指令、管理内存、管理 IO。当一个程序被运行,其代码被从磁盘加载到内存中,这时就开启了一个进程,进程可以视为程序的一个实例。大部分程序可以同时运行多个实例进程,而有些程序只能启动一个实例进程。程序是死的、静态的,进程是活的、动态的。进程可以分为系统进程和用户进程,系统进程用于完成操作系统的各种功能,而用户进程则是由用户启动的进程。
线程
线程必须依赖于进程而存在,线程是进程中的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的、能独立运行的基本单位。线程自己基本上不拥有系统资源,,只拥有在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个进程可以拥有多个线程,一个线程必须有一个父进程。线程,有时也被称为轻量级进程。
CPU 核心数和线程数的关系
目前主流 CPU 都是多核的,线程是 CPU 调度的最小单位。在同一时刻,一个 CPU 核心只能运行一个线程,因此 CPU 内核和同时运行的线程数是 1:1 的关系。这意味着一个 8 核 CPU 同时可以执行 8 个线程的代码。然而,Intel 引入了超线程技术后,产生了逻辑处理器的概念,使核心数与线程数形成 1:2 的关系。因此,对于一个具有 6 个内核的 CPU,逻辑处理器数是 12。
在 Java 中,可以通过调用 Runtime.getRuntime().availableProcessors() 方法来获取当前的 CPU 核心数,需要注意的是,这个核心数指的是逻辑处理器数。
上下文切换
操作系统要在多个进程(线程)之间进行调度,而每个线程在使用 CPU时总是要使用 CPU 中的资源,比如 CPU 寄存器和程序计数器。这就意味着,操作系统要保证线程在调度前后的正常执行,所以,操作系统中就有上下文切换的概念,它是指 CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。
引发上下文切换的原因一般包括:线程、进程切换、系统调用等等。上下文切换通常是计算密集型的,因为涉及一系列数据在各种寄存器、 缓存中的来回拷贝。就 CPU 时间而言,一次上下文切换大概需要 5000~20000 个时钟周期,相 对一个简单指令几个乃至十几个左右的执行时钟周期,可以看出这个成本的巨大。
并行和并发
并发 Concurrent:指应用能够交替执行不同的任务,比如单 CPU 核心下执行多线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉到的速度不断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算机的速度太快,我们无法察觉到而已。
并行 Parallel:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边打电话,这两件事情可以同时执行