天天看点

Volatile关键字的效果 Java内存模型

今天复习了下java内存模型,原子性,可见性,有序性。以下是概念。

什么是原子性:

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

一个很经典的例子就是银行账户转账问题: 

比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。

我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。 

原子性其实就是保证数据一致、线程安全一部分,

什么 可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。 

是有序性

程序执行的顺序按照代码的先后顺序执行。

一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:

int a = 10;    //语句1

int r = 2;    //语句2

a = a + 3;    //语句3

r = a*a;     //语句4

则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4

但绝不可能 2-1-4-3,因为这打破了依赖关系。

显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

J ava内存模型

共享内存模型指的就是Java内存模型(简称JMM),

JMM决定一个线程对共享变量的写入时 ,能 对另一个线程可见

。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:

线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本

。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。

2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。

以下是2个对比例子:

package classForm;

public class VolatileDemo {

 public static void main(String[] args) throws InterruptedException {

  ThreadVolatileDemo t1 = new ThreadVolatileDemo();

  t1.start();

  Thread.sleep(300);

  t1.isRun(false);

 }

}

class ThreadVolatileDemo extends Thread{

 private volatile boolean flag=true;

 @Override

 public void run() {

  System.out.println("子线程开始运行");

  while(flag){

  }

  System.out.println("子线程结束运行");

 public void isRun(boolean flag){

  this.flag = flag;

  System.out.println("ThreadVolatileDemo==>"+flag);

以上总结:例子中的flag是在子线程当中,主线程更新了子线程当中的flag的值,但是实际循环当中那个flag读取的值是存在主线程当中的,volatile的作用就是将子线程当中被更新的flag强制刷新到主线程的flag当中,这样其他线程访问的时候就可以读取到主线程当中最新的那个flag值了。