JAVA内存模型,java模型
JAVA内存模型,java模型
1.简述
Java内存模型即Java Memory Model,简称JMM。jmm中的主内存、工作内存与jvm中的Java堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的。
2.内部原理
线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。
3.支撑Java内存模型的基础原理
⑴指令重排序
在执行程序时,为了提高性能,编译器和处理器会对指令做重排序,在单线程中由于会保证数据依赖性,执行结果并不会变化,但是多线程情况下就会出现问题,volatile关键字除了之前提到的保证变量的内存可见性之外,另外一个重要的作用便是局部阻止重排序的发生,即保证被volatile关键字修饰的变量编译后的顺序与 也即是说使用了volatile关键字修饰,就不会出现因为重排序而可能造成的异常。
⑵内存屏障(Memory Barrier )
通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:
①保证特定操作的执行顺序。
②影响某些数据(或则是某条指令的执行结果)的内存可见性。
编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。
Memory Barrier所做的另外一件事是强制刷出各种CPU cache,如一个Write-Barrier(写入屏障)将刷出所有在Barrier之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。
这和java有什么关系?上面java内存模型中讲到的volatile是基于Memory Barrier实现的。
如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。这意味着,如果写入一个volatile变量,就可以保证:
①一个线程写入变量a后,任何线程访问该变量都会拿到最新值。
②在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。
⑶happens-before
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在happens-before关系。
首先先来个栗子:
i = 1; //线程A执行
j = i ; //线程B执行
线程B的j是否为1呢,这要看线程A的操作(i = 1)happens-before线程B的操作(j = i),happens-before原则定义如下:
①如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
②两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
happens-before原则基本规则:
①程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
②锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
③volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
④传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
⑤线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
⑥线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
⑦线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
⑧对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
然后再推导六条规则:
①将一个元素放入一个线程安全的队列的操作Happens-Before从队列中取出这个元素的操作
②将一个元素放入一个线程安全容器的操作Happens-Before从容器中取出这个元素的操作
③在CountDownLatch上的倒数操作Happens-Before CountDownLatch#await()操作
④释放Semaphore许可的操作Happens-Before获得许可操作
⑤Future表示的任务的所有操作Happens-Before Future#get()操作
⑥向Executor提交一个Runnable或Callable的操作Happens-Before任务开始执行操作
如果两个操作不存在上述(前面8条 + 后面6条)任一一个happens-before规则,那么这两个操作就没有顺序的保障。
如果操作A happens-before操作B,那么操作A在内存上所做的操作对操作B都是可见的。
相关文章
- 暂无相关文章
用户点评