美文网首页
java的内存模型

java的内存模型

作者: chengcongyue | 来源:发表于2019-04-08 16:56 被阅读0次

1,硬件的效率与一致性

计算机的存储设备和处理器的运行速度之间有几个数量级的差距,所有现代计算机加入了高速缓存(cache)来当做处理器和内存的缓冲,这样很好的解决了处理器和内存的矛盾,这是就引出了新的问题:缓冲一致性.
在多处理器的系统中,每一个处理器都有自己的缓存,但他们公有一个主内存,当他们的任务都涉及到同一块内存区域时,就有可能导致数据不一致的情况.
为了解决一致性的问题,在读写时根据一致性协议进行操作


摘自书籍

同时为了保证运算单元能够被充分的利用,可能会对内存进行乱序执行优化(Out-of-Order Execution),计算时乱序执行,运算完成后进行重组.这时如果一台计算机的任务依赖于另一台计算机的中间结果,其顺序性就不能通过代码的先后顺序来维护了

2,java内存模型

目的:屏蔽掉硬件和操作系统的内存访问差异,以实现在各个平台下都能达到一致的并发效果

2.1主内存和工作内存

内存模型的目的定义各个变量的访问规则,即定义存入到内存和从内存中读取的具体细节,此时的变量包括实例字段,静态字段和数组对象的元素,但是不包括局部变量和方法的参数,因为后面两个字段都是线程私有的,不会被共享,自然也不存在竞争关系
所有的变量都要存储在主内存中,每一条线程都有自己的要用到的变量的主内存的副本拷贝.
线程对变量的操作都是在工作内存中进行的,不能直接读写主内存的变量
两个线程的变量也都是互相不可见的,只有借助主内存才能够相互访问

类比上面的图片
注意:内存模型和内存区域不是一个层次上的东西

2.2 内存间的相互操作

即主内存和工作内存的之间的数据传递如何进行,具体的操作如下:
lock(锁定):
作用于主内存的变量,将一个变量标识为一条线程独占的状态
unLock(解锁):
作用于主内存的变量,从一条线程独占的状态中释放出来,这样其他的线程才能够锁定
read
作用于主内存的变量,将变量从主内存传入工作内存,以便后面的load动作使用
load
作用于工作内存的变量,将load到工作内存的变量放入到工作内存的副本中
store
作用于工作内存的变量,将变量传输到主内存中
write
将store入主内存的变量,放入到主内存的变量中
use
作用于工作内存的变量,将变量传递给工作引擎
assign
作用于工作内存的变量,将工作引擎的变量赋值给工作变量

2.2.1 分析

read,load是一组,store和write是一组,都是要顺序执行的,但不一定是连续执行
同时要遵守下面的规则:
1. read load,store write不能够单独出现
2. 工作内存修改完一定要同步到主内存
3.没有发生assign操作,不能够写回
4.在assign和load之后,才能够执行use和store的操作
5.一个时刻值允许一条线程lock,但一个lock可以被重复执行多次
???


摘自书籍

3 volatile型变量的特殊规则

通俗的讲,当一个变量声明为volatile的变量时,改变了它的可见性,其他的线程是立即可知的.但是普通的变量需要先传递给主内存

3.1

变量对于所有的线程都是一致的,但是并不能保证线程的安全性,因为无法保证操作的原子性,如下的代码:

public class VolatileTest{

    public static volatile int race=0;
    
    public static void increase()
    {
       race++;  
    }
    private static final int THREAD_COUNT=20;
    
    public static void main(String[] args) {
        Thread[] threads=new Thread[THREAD_COUNT];
        for (int i = 0; i < threads.length; i++) {
            threads[i]=new Thread(new Runnable() {
                public void run() {
                    for (int j = 0; j < 10000; j++) {
                        increase();
                    }
                }
            });
            threads[i].start();
        }
        while(Thread.activeCount()>1)
        {
            Thread.yield();
        }
        System.out.println(race);
    }
}

运行结果:小于20万的值
原因:i++不是原子操作


摘自书籍

当我们执行getstatic的操作时,我们将race变量放在了栈顶,此时的race变量是正确的,但是当执行iconst_1,iadd的指令的时候,其他的线程可能就已经将race变大了,最后放回的值就是较小的值

getstatic
iconst_1
iadd
putstatic
return  

3.2 volatile操作的适用场景

volatile boolean flag;
public void  shutdown()
{
  flag=true;
} 
public void doWork()
{
while(!flag){ //.....
}
}

3.3 volatile的第二个语义

volatile的第二个语义就是禁止重排序.普通的程序只能保证最后运行得到正确的结果,但是不能保证运行的过程中和代码中的顺序一致."线程内表现为串行的语义"


图片.png

3.4 volatile的意义

在某些情况下,确实比锁要快,但是虚拟机对锁实行了优化,所以很难说快多少
在读操作和普通变量没啥区别,但是在写操作会慢一点,因为在本地插入了许多的内存屏障

相关文章

网友评论

      本文标题:java的内存模型

      本文链接:https://www.haomeiwen.com/subject/gzlpiqtx.html