概述
为什么需要多任务处理
计算机的运算速度与它的存储和通信子系统的速度差距太大,大量的时间花费在网络通信,磁盘IO等
TPS:每秒事务处理数,代表一秒内服务端平均能响应的请求总数,可以用来衡量一个服务性能的高低好坏。
硬件的效率与一致性
大多数的运算任务都不可能只靠处理器的计算来完成。处理器至少要与内存交互,如读取数据,存储运算结果等。由于计算机的存储设备与处理器的计算有着几个数量级的差距,所以现在计算机不得不加入一层或多层读写速度尽可能接近处理器运算速度的高速缓存来作为内存与处理器之间的缓冲。
缓存同时带来了一致性问题。为了解决一致性问题,需要处理器遵循一些协议,在读写时根据协议来进行操作,这类协议有MSI,MESI等。
内存模型可以理解为特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。不同架构的物理机器可以拥有不一样的内存模型,JAVA虚拟机也有自己的内存模型。
为了使运算单元充分利用,处理器可能会对输入的代码进行乱序执行。
主内存与工作内存
内存模型的主要目的是定义程序中各种变量(非线程私有) 的访问规则,即关注在虚拟机钟把变量值存储到内存和从内存中取出变量值的这样底层细节。JAVA内存模型并没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器是否要进行调整代码执行顺序这类优化措施。
JAVA内存模型规定了所有的变量存储在主内存(Main Memory)中。每条线程的还有自己的工作内存(Work Memory)。线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对于变量的所有操作都需要在工作内存中进行,而不能直接读取主内存中的数据。不同线程也访问对方工作内存中的变量。线程间变量值的传递均需要通过主内存来完成。
内存间交互操作
关于主内存与工作内存之间具体的交互协议,JAVA虚拟机定义了8种操作来完成,JAVA虚拟机必须保证下面提及的每一种操作都是原子的,不可再分的。(对于double和long类型的变量来说,load,store,read,write操作在某些平台上会有例外)
- 作用于主内存
- lock(锁定):把一个变量标识为一条线程独占的状态
- unlock(解锁):把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程使用。
- read(读取):把一个变量的值从主内存读取到工作内存中,方便以后的load调用
- write(写入):把store操作从工作内存中得到的变量值放入到主内存的变量中
- 作用于工作内存
- load(载入):把read操作从主内存中得到的变量值放到工作内存中
- use(使用):把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作
- assign(赋值):把一个执行引擎中接收的值赋给工作内存的变量。每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作
- store(存储):把工作内存中一个变量的值传送到主内存中,以便write操作
Volatile关键字
- 保证此变量对所有线程的可见性
- 使用之后,会重新写入缓存,其他线程的该缓存失效,重新读主内存。store-write操作。
- 除此之外,Synchronized和final也支持
- 禁止指令重排序优化(提供一个内存屏障来避免这个问题,不等同于垃圾回收时候的内存屏障)
Long和Double的特殊规则
- JAVA内存模型要求8种操作是原子性操作,但对于64位的Long和Double允许分成俩次读写操作,所以不一定是原子性操作
线程
实现线程的三种方式
- 内核线程
- 1:1实现。由操作系统内核直接支持的线程,这种线程由内核来完成线程切换,内核通过调度器来对线程进行调度,并将线程的任务映射到处理器上。
- 程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口-轻量级进程,也就是我们平常所说的线程。每一个轻量级进程都会有一个内核线程来支持。
- 由于内核线程的支持,每一个轻量级进程都是独立的单元。即时一个轻量级进程在系统调用堵塞了,也不会影响整体。
- 局限性在于因为依靠内核线程,所以线程的各种操作都需要系统调用,而系统调用的代价比较高,需要在用户态,核心态之间切换,同时也会消耗一定的核心资源。
- 用户线程
- 1:N实现。指的是建立在用户空间的线程库上,系统内核不能感受用户线程的存在及如何实现。
- 用户线程的线程的相关操作都是完全在用户态中完成的,不需要内核的帮助。
- 因为没有内核的帮助,所以线程的创建,调度,销毁等都需要用户自己处理。以及阻塞如何处理,跨处理器的映射等
- 混合线程
- M:N实现。轻量级进程+用户线程。
- 用户线程还是建立在用户空间,所以线程的相关创建等操作依然廉价,可以支持大规模的用户并发。
- 轻量级进程充当用户线程与内核线程之间的桥梁,这样可以提供内核线程的调度及映射功能,并且用户线程的系统调用要通过轻量级进程来调用,降低系统阻塞的风险。
JAVA线程的实现
纤程
|