线程
进程和线程的区别
- 操作系统以进程为单位,分配 cpu 资源,进程是资源分配的最小单位
- 线程是轻量级进程,是操作系统调度执行的最小单位
- 线程可以共享进程内资源,进程资源是独立的
- 进程间通信
- 管道
- 信号
- 消息队列
- 共享内存
- 信号量
- 套接字
操作系统中的线程
线程间会有同步互斥
同步互斥控制方法
- 临界值
- 互斥量
- 信号量
- 事件
上下文切换
- 指 CPU 从一个进程或线程到另一个线程或进程切换
- 只能在内核模式下发生
- 是多任务的一个特性
- 成本很高
内核态和用户态
- 内核态是完全且不受限制的访问硬件,为操作系统的最低级别
- 用户态是不能直接访问硬件或引用内存,需要委托给系统 api 访问。
- 系统如何切换为内核态
- 系统调用
- 异常事件
- 设备中断
操作系统线程生命周期
- 初始
- 就绪
- 运行
- 休眠
- 终止
java 中线程的生命周期
- 初始
- 就绪 (可运行与运行)
- 阻塞
- 等待
- 带时间等待
- 终止
java 中线程
- 线程实现方式
- 使用或继承 Thread 类
- runnable 接口 —将线程和任务隔离
- callable 接口 —可以获取返回值,可以获取异常,一般结合线程池使用
- lambda 直接生成
- 线程实现方式
协程
- 基于线程,但比线程更轻量级,不被操作系统内核管理,而完全用程序控制,对于内核有不可见特性。
- 协程适用于被阻塞,且需要大量并发场景 (网络 IO),不适合大量计算场景
java 线程的调度机制
线程调度分别是协同式和抢占式
- 协同式是线程执行时间由线程本身决定的,执行完了通知系统切换。
- 优点是实现简单,切换操作对线程可知,无线程同步问题。
- 缺点是线程执行时间不可控,如出现问题,就可能一直阻塞
- 抢占式是有系统分配执行时间,线程切换不由线程本身决定
- java 使用的是抢占式,java 共有 **10 种 ** 线程优先级,但不靠谱,线程还是用系统调用的,主要依靠的是操作系统的调用
- 协同式是线程执行时间由线程本身决定的,执行完了通知系统切换。
Thread 常用方法
- sleep
- 会让线程从 Running 进入 TIMED_WAITING,不会释放锁
- 其他线程可以使用 interrupt 打断睡眠线程,sleep 会抛出 InterruptedException,并且清除中断标志
- 睡眠结束后线程不一定会立刻执行
- 如传入时间为 0,则和 yield 相同
- yield
- yield 会释放 cpu 资源,是线程进入 runnable 状态,让优先级更高的线程获得执行机会,不会释放锁
- main 调用只会继续执行,因为 main 优先级最高
- 具体实现依赖于操作系统的任务调度
- join
- 让主线程 waiting,一直等到其他线程不再活动为止
- stop
- 会停掉线程同时会释放锁,不推荐,会导致线程不安全
- sleep
java 中断机制停止线程
- 中断是一种协作机制,即通过中断并不能直接终止另一个线程,而是由被中断的线程自己处理是否中断。
- API 的使用
- interrupt,将线程的中断标志设置为 true,不会停止线程
- isInterrupt,判断当前线程中断标志是否为 true,不会清除中断标志
- Thread.interrupted(),判断当前线程中断标志位是否为 true,并清除中断标志,重置为 false
- sleep 和 wait 也会清除中断标记,如果需要使用中断机制,每次 catch 中都需要将中断标志补回为 true
java 的线程间的通信
volatile 关键字
等待唤醒机制
可以基于 wait 和 notify 进行实现
- 缺点一,必须在 synchronized 中执行,依靠的锁
- 缺点二,多线程等待,不一定唤醒指定线程,即使用 notifyAll 去唤醒全部线程,也是让其他线程去抢占
也可以通过
LockSupport
,它是 JDK 用于实现线程阻塞和唤醒的工具,线程调用 park 则等待“许可”,调用 unpark 则指定线程提供“许可“。
- 通常使用这个方法
管道输入输出流
join
细节点
- Linux 没有线程,后续单独写了一套,又称轻量级进程。
- nginx 是多进程执行的。
进程间的通讯
同一台计算机间的为 IPC
管道机制
- 匿名管道:用于父子进程之间的通信
- 命名管道:无关系进程间可以通过命名来查询
信号
软件上对中断机制的模拟
消息队列
共享内存
需要考虑同步操作
信号量
套字节 (socket)
mysql 使用该种方式,不需要通过网络协议栈,没有打包拆包校验等操作,效率更高
mysql 中
tmp.sock
文件用于创建 socket
不同计算机间为 RPC
如 dubbo 框架、Http 协议也常用于 RPC,如 SpringCloud 微服务
CPU 核心数和线程数
Intel 中引入了逻辑处理器的概念,使核心数和线程数有 1:2 的关系
java 中
Runtime.getRuntime().availableProcessors()
,可以获得当前 cpu 核心数(逻辑处理器核心数)这个和并发编程中设置线程数量关联很大
上下文切换
上下文就代表不同线程或者进程的数据,在 cpu 切换的时候,需要将这种数据(可理解为局部变量)存入内存中