Java内存模型 vs JVM运行时数据区
为什么java 能在不同的平台上跑?因为字节码是在虚拟机上跑,所以平台无关。遵循《Java虚拟机规范》
初看Java内存模型
来看看大家都是怎么定义的 :
Java虚拟机可以同时支持多个执行线程,若未正确同步,线程的行为可能会出现混淆和违反直觉。
多线程程序的语义,它包含了,当多个线程修改了共享内存中的值时,应该读取到哪个值的规则。由于这部分规范类似于不同硬件体系结构的内存模型,因此这些语义称为Java编程语言内存模型。
这些语义没有规定如何执行多线程程序。相反,它们描述了允许多线程程序的合法行为。
多线程中的问题
- 所见非所得
- 无法肉眼去检测程序的准确性
- 不同的运行平台有不同的表现
- 错误很难重现
有两个线程t1 和t2 ,一个标记, 当t1更改标记后,t2 没有读到更改后的标记。加上volatile关键字就收到了,示例代码如下。
public class Test1 {
private volatile boolean flag = true ;
public static void main(String[] args) throws InterruptedException {
Test1 demo1 = new Test1();
System.out.println("代码开始了");
new Thread(new Runnable() {
@Override
public void run() {
int i = 0 ;
while (demo1.flag){
i ++;
}
System.out.println("run i = "+ i);
}
}).start();
TimeUnit.SECONDS.sleep(2);
demo1.flag = false ;
System.out.println("被设置为false了");
}
}
//客户端输出
代码开始了
被设置为false了
run i = 201004010
复制代码
flag 的值 ,没有读到 ,可能原因是主线程没写入,或者副线程没读到。 flag 放在CPU的高速缓存中,时间片上有一瞬间值不一致,很快,所以不是高速缓存的锅 ,那是谁的呢,CPU指令重排。
CPU指令重排序
Java编程语言的语义允许Java编译器和微处理器进行执行优化,这些优化导致了与其交互的代码不再同步,从而导致看似矛盾的行为。
单线程的指令重排-没有问题
循环的效率大于缓存,cpu想出来用赋值进行指令重排,A=0, B=0 。
多线程的指令重排-有问题
0012 变成了 1122 。
JIT编译器(Just In Time Compiler)
脚本语言与编译语言的区别 ?
解释执行:即咱们说的脚本,在执行时,由语言的解释器将其一条条翻译成机器可识别的指令 。翻译的价格有200块,500块不等。
编译执行:将我们编写的程序,直接编译成机器可以识别的指令码。
_Java是脚本语言还是编译语言?Java_介于脚本语言与编译语言之间
volatile 关键字
可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到。
Java内存模型规定
对volatile变量v的写入,与所有其他线程后续对v的读同步
要满足这些条件,所以volatile关键字就有这些功能:
禁止缓存:
- volatile变量的访问控制符会加个ACC_VOLATILE 从链接得知 ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
- 对volatile变量相关的指令不做重排序
Shared Variables 定义
可以在线程之间共享的内存成为共享内存或堆内存。
所有实例字段、静态字段和数组元素都存储在堆内存中,这些字段和数组都是标题中提到的共享变量。
冲突:如果至少有一个访问是写操作,那么对同一个变量的两次访问是冲突的
这些能被多个线程访问的共享变量是内存模型规范的对象。
线程间操作的定义
- 线程间操作指:一个程序执行的操作可被其他线程感知或被其他线程直接影响。
- Java内存模型只描述线程间操作,不描述线程内操作,线程内操作按照线程内语义执行。
所有线程间操作,都存在可见性问题,JMM需要对其进行规范
对于同步的规则定义
- 对volatile变量v的写入,与所有其他线程后续对v的读同步
- 对于监视器m的解锁与所有后续操作对于m的加锁同步
- 对于每个属性写入默认值(0,false,null)与每个线程对其进行的操作同步
- 启动线程的操作与线程中的第一个操作同步
- 线程T2的最后操作与线程T1发现线程T2已经结束同步。(isAlive,join可以判断线程是否终结)
Happens-before先行发生原则
final 在JMM 中的处理
Word Tearing 字节处理
double和long的特殊处理
再看Java内存模型