参考:深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)
“内存模型”一词,可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。
Java内存模型(Java Memory Model, JMM)
主内存与工作内存
每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成.
主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。
内存间交互操作
lock
unlock
read
load
assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
use
store
write
如果要把一个变量从主内存复制到工作内存,那就要顺序地执行read和load操作,如果要把变量从工作内存同步回主内存,就要顺序地执行store和write操作。注意,Java内存模型只要求上述两个操作必须按顺序执行,而没有保证是连续执行。也就是说,read与load之间、store与write之间是可插入其他指令的,如对主内存中的变量a、b进行访问时,一种可能出现顺序是read a、read b、load b、load a。
volatile型变量的特殊规则
第一是保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
假定T表示一个线程,V和W分别表示两个volatile型变量,那么在进行read、load、use、assign、store和write操作时,,每次使用V前都必须先从主内存刷新最新的值,用于保证能看见其他线程对变量V所做的修改后的值。工作内存中,每次修改V后都必须立刻同步回主内存中,用于保证其他线程可以看到自己对变量V所做的修改。
第二是是禁止指令重排序优化。
volatile修饰的变量不会被指令重排序优化,保证代码的执行顺序与程序的顺序相同。
特征:原子性、可见性与有序性
这个模型的特征。Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的。
原子性(Atomicity):由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write,我们大致可以认为基本数据类型的访问读写是具备原子性的。
可见性(Visibility):可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的。
有序性(Ordering),Java程序中天然的有序性可以总结为一句话:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行的语义”(Within-Thread As-If-Serial Semantics),后半句是指“指令重排序”现象和“工作内存与主内存同步延迟”现象。
先行发生原则
先行发生是Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。
Java与线程
线程是CPU调度的基本单位。
Windows版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程之中,因为Windows和Linux系统提供的线程模型就是一对一的
Java线程调度
协同式线程调度(Cooperative Threads-Scheduling)和抢占式线程调度(Preemptive Threads-Scheduling)
协同式线程调度 线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上。坏处也很明显:线程执行时间不可控制,甚至如果一个线程编写有问题,一直不告知系统进行线程切换,那么程序就会一直阻塞在那里。
抢占式调度的多线程系统,那么每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定(在Java中,Thread.yield()可以让出执行时间,但是要获取执行时间的话,线程本身是没有什么办法的)。
线程状态
新建(New)
运行(Runable)
无限期等待(Waiting)
限期等待(Timed Waiting)
阻塞(Blocked)
结束(Terminated)
本章中,我们首先了解了虚拟机Java内存模型的结构及操作,然后讲解了原子性、可见性、有序性在Java内存模型中的体现,最后介绍了先行发生原则的规则及使用。另外,我们还了解了线程在Java语言之中是如何实现的。