Java内存模型学习笔记

2019-12-14  本文已影响0人  ssochi

目标

Java内存模型的目标是定义程序中各个变量的访问规则.即虚拟机对变量在内存中存取的规则.

目的

Java内存模型的目的是屏蔽各种硬件和操作系统的内存访问差异,使得Java在各个平台下能达到一致的内存访问效果.

主内存与工作内存

Java线程 <===>工作内存<===>Save&Load<===>主内存

进一步理解,主内存可以理解为Java堆,而工作内存可以理解为栈,但工作内存还有一部分属于高速缓存.

内存间的交互操作

共有八种操作:lock,unlock,read,load,use,assign,store,write
一下是对这些操作的限制:

volatile的特殊规则

可见性

在volatile标记的变量使用前必会调用read->load->use
在volatile标记的变量赋值后,会调用assign->store->write
也就是说,被volatile标记的变量标记的对象会及时的刷新主内存中它的值,并在工作内存中使用主内存中最新的值.
但由于从use到变量真正的使用往往不是原子性操作,因此单纯的使用volatile也往往不是线程安全的

有序性

在计算机执行指令时,会对指令就行重排序,而volatile关键字能阻止这个行为。在volatile之前的操作不会再它之后执行,而在volatile之后的操作不会在它之前执行。典型的例子就是双重判断的单例模式,对instance加volatile关键字的目的就是防止对象初始化和对象赋值的重排序。

final

final与volatile相同也具备可见性和有序性。

Happens-Before规则

举个例子,有一下两个线程:
线程一:

    y = 1
    lock M
    x = 1
    unlockM

线程二:

    lock M
    i = x
    unlockM
    j = y

如果线程1的unlockM先于线程2的lockM
那么我们可以推到出线程1的所有操作先于线程2;
但是如果去掉lock和unlock代码:

    y = 1
    x = 1

线程二:

    i = x
    j = y

如果x = 1 先于 i = x;我们并不能推出y = 1 先于 j = y;

再举个简单的例子,看如下代码:

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!shutdown);
                System.out.println(count);
            }
        }).start();

        Thread.sleep(100);
        for (int i = 0; i < 500; i++) {
            count++;
        }
        shutdown = true;

如果shutdown不为volatile,那么大概率这个进程永远也无法结束,因为shutdown不具备可见性。即使这个进程成功停止了,输出的count也是不确定的。
但如果shutdown是volatile,那么输出的count必定等于500,因为我们能确定对shutdown的写操作一定先于对shutdown的读操作,那么在对shutdown写操作之前的所有操作也应该先于对shutdown读操作之后的所有操作,因此,对count的写操作就会先于对count的读操作。

上一篇 下一篇

猜你喜欢

热点阅读