java多线程编程(五)
java内存模型分类
使用写缓冲区临时保存向内存中写入数据。
Why?
提升效率:
1)写缓冲区可以保证指令流水线持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟
2)通过以批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总线的占用。
缺点:
每个处理器上的写缓冲区,仅仅对它所在的处理器可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行顺序,不一定与内存实际发生的读/写操作顺序一致
eg:
处理器/示例项 | ProcessorA | ProcessorB |
---|---|---|
代码 |
a=1; //A1</br>x=b;//A2 |
b=2;//B1</br>y=a;//B2 |
运行结果 |
初始状态:a=b=0;</br>处理允许执行后得到的结果:x=y=0 |
具体原因如下:
处理器A和处理器B可以同时把共享变量写入到自己的写缓存区(A1,B1),然后从内存中
读取另一个共享变量(A2、B2),最后才把自己写缓存区中保存的脏数据刷新到内存中(A3、B3)当以这种时序执行时,程序就可以得到x=y=0的结果。
从内存操作实际发生的顺序来看,直到处理器A执行A3来刷新自己的写缓存区,写操作
A1才算真正执行了。虽然处理器A执行内存操作的顺序为:A1→A2,但内存操作实际发生的顺
序却是A2→A1。此时,处理器A的内存操作顺序被重排序了(处理器B的情况和处理器A一样,
这里就不赘述了)。
这里的关键是,由于写缓冲区仅对自己的处理器可见,它会导致处理器执行内存操作的
顺序可能会与内存实际的操作执行顺序不一致。由于现代的处理器都会使用写缓冲区,因此现代的处理器都会允许对写-读操作进行重排序
happens-before简介
在JMM中,如果一
个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关
系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。
与程序员密切相关的happens-before规则如下:
程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的
读。传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
happens-before与JMM关系如下: