线程基础知识
线程与进程
进程
操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。
当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
线程
线程,有时被称为轻量级进程,是操作系统调度(CPU调度)执行的最小单位。
线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。
一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行 。
【面试题】进程与线程的区别
(1)进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
(2)进程拥有共享的资源,如内存空间等,供其内部的线程共享
(3)进程间通信较为复杂
- 同一台计算机的进程通信称为 IPC(Inter-process communication)
- 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
(4)线程间通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
(5)线程更轻量,线程的上下文切换成本更低
进程间通信的方式
相信大学期间有准备过408的小伙伴应该很熟悉!大致是有6种方式。
1. 管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
2. 信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的
3. 消息队列(message queue):生产者与消费者之间的通信
4. 共享内存(shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
5. 信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。(线程级别也可以!)
6. 套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
在开发中有一个很常见的场景,就是分布式锁(redis,mysql,zk)!进程与进程之间通信也可以通过操作共享内存的方式去实现。
线程的同步互斥
线程同步
是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
举一个例子
我们开发一个网页,用户在浏览器操作,浏览器就会向Tomcat服务器发请求,Tomcat可以将请求转发到一个具体的应用(应用中也有线程),可能会去查MySQL数据库。
浏览器线程、tomcat线程、应用中的线程、数据库线程,它们之间相互协作去完成请求与响应,这其实就是一个同步的过程。
线程互斥
线程互斥对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用(例如:synchronized ),其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。
上下文切换
上下文切换是指CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。
上下文切换步骤
- 暂停一个进程的处理,并将该进程的CPU状态(即上下文)存储在内存中的某个地方
- 从内存中获取下一个进程的上下文,并在CPU的寄存器中恢复它
- 返回到程序计数器指示的位置(即返回到进程被中断的代码行)以恢复进程。
上下文切换只能在内核模式下发生。
内核模式是CPU的特权模式,其中只有内核运行,并提供对所有内存位置和所有其他系统资源的访问。
其他程序(包括应用程序)最初在用户模式下运行,但它们可以通过系统调用运行部分内核代码。
【面试题】什么情况下会发生上下文切换?
- 线程、进程切换
- 系统调用,当发生某些预先不可知的异常时,就会切换到内核态,以执行相关的异常事件。
- 中断,在使用外围设备时,如外围设备完成了用户请求,就会向CPU发送一个中断信号,此时,CPU就会暂停执行原本的下一条指令,转去处理中断事件。此时,如果原来在用户态,则自然就会切换到内核态。
具体看这里:
内核模式(Kernel Mode)vs 用户模式(User Mode)
操作系统层面线程生命周期
操作系统层面的线程生命周期基本上可以用下图这个“五态模型”来描述。这五态分别是:初始状态、可运行状态、运行状态、休眠状态和终止状态。
这个五态模型也是我们之前学习操作系统的时候课本里面讲的(当然还有“七态模型”)。
这里我们直接用网图:
五态模型
七态模型
但是需要注意的是,不同的语言规范,这些状态模型的数量、名称都会发生改变!!!我们这里就用Java为例
Java中的状态模型
看Thread源码可以发现,在Java中线程一共有6种状态
public enum State {
// 创建态
NEW,
// 运行态 + 就绪态
RUNNABLE,
// 阻塞态(synchronized)
BLOCKED,
// 等待(join、wait、park)
WAITING,
// 有时限的等待(sleep)
TIMED_WAITING,
// 终止态
TERMINATED;
}