文章目录
目录
前言
主要介绍Java内存模型和线程。
一、并发处理广泛发展根本原因
- Amdahl定律(多核处理器中并行、串行占比重)代替摩尔定律(晶体管数),成为决定计算机性能发展的源动力
二、Java虚拟机中的并发
- 并发原因
- 计算机CPU运算速度远远快于其存储(与内存交互)和通信子系统(IO),串行执行线程会使得CPU大多数时间处于等待
- 让计算机并行处理不同线程的IF/DEC/EXE/ME/WB(指令流水线技术)操作,可以充分利用CPU,大大提高效率
- 硬件并发问题
- 缓存一致性(可见性) + 乱序执行优化(有序性)
- Java内存模型
- 目的
- 定义程序中变量{(实例数据+静态数据+数组对象的元素等存在线程共享的堆和方法区中的数据)不包括(方法参数+方法变量存在线程独占的局部变量表)}的访问规则
- 主内存+工作内存
- 主+工 引发问题
- 会导致不可见问题(工作内存和主内存不一致)
- 主+工 交互协议
- lock主、unlock主、read主、load工、use工、assign工、store工、write主
- { read主、load工、use工、assign工、store工、write主 } 六个操作直接保证基本数据类型(除long、double)的原子性
- { lock主、unlock主 }没有开放给用户使用,但是可以通过synchronized关键字给对象绑定底层monitor,实现原子性
- 主+工 引发问题
- volatile
- 作用
- 解决可见性+有序性问题
- 原理
- 可见性
- 读屏障,读代码之前加入,保证之后的代码一定访问内存中最新数据(volatile变量修改对其他处理器立即可见)
- 有序性
- 写屏障,赋值操作之后,保证之前的代码不能被指令重排优化到后面
- 可见性
- 注意
- volatile也有工作内存的拷贝,其特定的执行顺序保证其可见性,看起来好像没有工作内存
- 作用
- long、double对象
- 64位可以分成两次操作完成访问,有可能出现非原子的访问行为,但是很少见,基本无需考虑
- happens-before先行发生原则
- 程序次序、管程锁定、volatile写读、线程启动、线程终止、线程中断、对象终结、传递性规则
- 目的
- Java与线程
- 线程实现
- 内核线程
- 内核线程KLT与轻量级进程LWP 1:1,会频繁发生用户态与核心态转换降低效率
- 用户线程
- 进程P包含多个用户线程UT 1:N,系统内核感受不到用户线程状态
- 混合
- 内核线程KLT连接轻量级进程LWP ,轻量级进程LWP连接用户线程UT ,KLT与UT N:M
- 内核线程
- Java线程调度
- 协同调度
- 一个线程执行完后主动调度,不会出现线程同步问题,但是可能导致一个线程一直执行
- 抢占式调度
- 系统分配执行时间,线程可以通过yield和priority来建议系统时间的分配,但是决定权在系统不在线程本身
- 协同调度
- 线程状态
- NEW、RUNNABLE、WAITING、TIMEDWAIT、BLOCK、TERMINATED
- 协程
- 定义
- 轻量级的用户线程,一般协同式调度
- 用法
传统创建新线程可以提高CPU利用率,但是线程切换导致的用户态与核心态切换资源消耗太大- 在线程执行长时间阻塞IO时,不创建新线程,而是创建用户态协程完成IO操作
- 协程 + 异步IO 可以达到最好的效率提升
- 分类
- 有栈协程
- 完整做调用栈的保护、恢复工作
- 无栈协程
- await、yield等关键字实现的有限状态机,将状态保存在闭包里
- 有栈协程
- 纤程
- 有栈协程的特例实现
- 利用纤程并发时,会分为执行过程 + 调度器两个部分,用户自行选择控制其中一个或两个部分
- 定义
- 线程实现