java内存模型
一个服务端需要同时对多个客户端提供服务,就是并法的具体场景。
衡量一个服务性能的好坏,每秒实务处理数(Transactions Per Second,TPS)是重要指标,它代表一秒内服务端平均能响应的请求总数。
硬件的效率和一致性
基于高速缓存的存储交互很好地解决了处理器和内存速度之间的矛盾,但是引入了缓存一致性的问题,在多路处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一个主内存,这种系统又叫做共享内存多核处理器。
需要多个处理器缓存和内存之间的协调,需要遵守缓存协议,有MSI,MESI等等。
java内存模型
《Java虚拟机规范》中曾经定义“Java内存模型”(JMM)来屏蔽各种硬件和操作系统的内存访问差异。
程序员只需要按照内存模型进行开发,虚拟机能够利用硬件的各种特性(寄存器,高速缓存和指令集中的特有指令)来实现模型细节。
JMM主要定义了各种变量的访问规则
即关注在虚拟机中把变量值存储到内存和从内存中取出的底层实现。
这里讨论的变量值是除了局部变量和方法参数的,因为两者存储在线程私有的栈中,不会有并法问题。
JVM会主动使用寄存器和缓存硬件来JMM。
在JMM中每个线程有自己的工作内存。
JMM定义了所有的变量都存储在主内存中,线程的工作内存中保存被该线程使用的变量的副本。
线程对变量的操作都必须在工作内存中进行,而不能操作主内存中的数据。
线程间的变量传递也需要主内存当中间人。
内存间的交互
数据从主内存到工作内存,工作内存到主内存。
JMM有8种原子操作:
- read+load:将变量从主内存变量加载到工作内存变量。
- use:将变量从工作内存变量载入到执行引擎。
- store+write将变量从工作内存变量写回到主内存变量中。
- assign:将变量从执行引擎赋值给工作线程变量。
- lock:将主内存的变量,标识为一条线程独占的状态。
- unlock
指令monitorexit和monitorenter就是lock和unlock的实现,synchronized就是使用的来实现的原子性
- read+load和store+write是一起出现的,但不要求连续,变量一定会写入线程内存或者写入主内存。
- 线程执行assign操作后必须要写回到主内存(即变量在工作内存中改变后必须要写回到主内存中。
- 一个变量只能从主内存中诞生,并且传入线程内存。
- 一个变量在同一时间内只允许一条线程对其lock操作,但线程可以执行多次lock操作,解锁都需要多次unlock。
- lock会清空工作内存中此变量的值。
- unlock之前必须将变量先同步回主内存中。
volatile:只能保证可见性
volatile值被一个线程修改后,其他线程立刻可见修改。(volatile值在各个线程中是一致的)。
但是volatile并不是线程安全的,因为取值和操作值,回写值不是一个原子操作,可能在写回之前其他线程已经修改过了。
volatile在JMM实现的逻辑:
- 每次使用volatile变量都需要从主内存中取到线程变量(use,load,read连续使用)。
- 在工作内存中的volatile变量修改后会立刻写回主内存。(store,write,assign连续使用)。
- volatile变量不会iu被指令重排序优化。
可见性
synchronized和final和volatile实现可见性。
synchronized在unlock之前将变量写回到主内存中。
final在构造器中初始化完成后,其他线程就能看到其值。
默认的JMM执行顺序
- 一个线程内,按顺序执行。
- 执行先后传递性。
Java线程
Java实现了对主流的操作系统的线程实现的封装,我们使用Java代码,而java代码调用Native方法。
映射方式:《java虚拟机规范》没有规范,各自实现。
java线程1:1映射操作系统轻量级进程
HotSpot采用,线程调度全权交给操作系统,只能设置线程优先级给操作系统提建议。