目录
1、什么是线程
2、为什么引入线程
2.1、线程的优缺点
3、CPU的工作原理
4、线程和进程的关系
4.1、线程和进程的入口函数
4.2、线程独享的资源
1、什么是线程
一个进程中可以有一个或者多个线程,每个线程都是一个独立的执行流。多个线程之间,也是并发(并行+并发)执行的。
这里的多线程可能是在多个CPU核心上,同时运行,也可能是在一个CPU核心上,通过快速调度进行运行。在第一个JavaEE的博客中说到进程的调度,指的是这些进程里面只有一个线程。操作系统中真正调度的是线程,而不是进程。
- 线程是操作系统调度运行的基本单位。
- 进程是操作系统资源分配的基本单位。
- 线程之间共享进程资源。
2、为什么引入线程
早在80年代,由于进程的创建和销毁以及切换存在较大空间的开销,因此人们急需一种轻型的进程技术来减少资源的开销,于是线程在这种背景之下产生了。
2.1、线程的优缺点
优点:
- 创建线程比创建进程更快
- 销毁线程比销毁进程更快
- 调度线程比调度进程更快
- 创建一个进程,进程中的线程可以共享进程的资源,资源的开销减少。
缺点:
虽然说线程之间的调度开销小,但是由于多个线程共享同一个进程中的资源,如果一个线程崩溃了,那么有可能导致整个进程被抹杀。但是进程之间的调度不存在这个问题,每个进程都是独立的空间,都有独立的资源,一个进程崩溃不会影响到其他进程的运行。
3、CPU的工作原理
要了解线程和进程之前我们还要了解一下一段代码在CPU中执行过程。
- CPU只知道两件事,1、从内存中读取指令,2、执行指令,执行完成之后,回到1.
- 注意这里计算器为了提高效率,CPU中存在寄存器,读取数据的时候他会从PC寄存器(Program Counter Register)中读取数据存放在内存中的地址。PC寄存器也就是程序计数器。程序计数器有寄存信息和计数两种功能结构。
- 我们写的代码通过编译器生成可执行文件(01代码),存放在磁盘中,内存中的指令是通过加载磁盘中的可执行文件形成的。
计算机在执行这些指令的时候,只需要找到函数被编译之后的第一条指令就可以了,这就是入口函数。
4、线程和进程的关系
4.1、线程和进程的入口函数
进程的入口函数就是main方法, 线程诞生之前进程中就只有一个执行流。线程诞生之后进程之中会存在多个线程。这个时候每个线程就需要自己的入口函数。
把CPU的PC寄存器指向线程的入口函数,这样线程就可以运行起来了,这就是为什么我们创建线程时必须指定一个入口函数的原因.
4.2、线程独享的资源
函数在别执行的时候产生的数据包括函数参数、局部变量、返回地址等信息,这些信息是保存在栈中的。操作系统要为每个线程在进程的地址空间中分配一个栈,即每个线程都有独属于自己的栈。
线程运行的本质其实就是函数的执行,函数的执行总有一个源头,这个源头就是所谓的入口函数。cpu从入口函数开始执行从而形成一个执行流。这个执行流就是线程。
- CPU执行指令的信息保存在程序计数器中,通过这个寄存器计算机就可以知道接下来执行那一条指令。由于多线程可以是并发进行的,所以一个线程在执行一段之后,可能会被停止,因此程序计数器需要保存下这个线程被执行都那个地方的信息,以便于下次要继续执行这个线程的时候,知道从哪里开始。
- 同时函数运行时需要额外的寄存器来保存一些信息,像部分局部变量之类。这些寄存器也是线程私有的,一个线程不可能访问到另一个线程的这类寄存器信息。
- 💥💥从上面的讨论中我们知道,到目前为止,所属线程的栈区、程序计数器、栈指针以及函数运行使用的寄存器是线程私有的。
- 以上这些信息有一个统一的名字,就是线程上下文(throead context)
除此之外,剩下的都是线程间共享资源。
❗❗❗总结线程的私有资源
具体来说,线程的私有资源有以下这些
- 线程ID:每个线程都有字节唯一的ID,用于区分不同的线程
- 寄存器组的值:当线程切换时,必须将原有的线程的寄存器集合的状态保存,以便重新切换时得以恢复。
- 线程的堆栈:堆栈是保证线程独立运行所必须的
- 错误返回码:由于同一进程中有很多线程同时运行,可能某个线程进行系统调用后设置了error值,而在该线程还没有处理这个错误,另一个线程就在此时被调度器投入运行,这样错误值就有可能别修改。所以,不同的线程应该拥有自己的错误返回码变量。
- 线程优先级:线程调度的次序(并不是优先级大的一定先执行,优先级大只是最先执行的机会大。线程的优先级就像个CPU提一个建议的左右,采不采用有CPU决定)