赞
踩
关于volatile关键字的话,工作中没有遇到使用场景,大部分是面试的时候必问的一个题目;
本文主要内容摘自《JAVA高并发编程详解》
CPU Cache主要是为了解决CPU与内存之间访问速度的差异问题,Cache则是在程序运行的过程中会将运算的所需数据从主内存复制一份到CPU Cache中,这样CPU在计算的过程中,可以直接从CPU Cache中读取或写入,当运算结束之后,CPU Cache再将最新数据刷新到主内存当中,CPU直接通过访问Cache的方式替代直接访问主存的方式,极大地提高了CPU的吞吐计算能力;
CPU Cache虽然解决了CPU与主存访问速度差异,但也引来了另外一个问题,就是缓存的一致性问题;
比如i++的整个计算过程是这样的:
在单线程的情况下,这个计算过程没有任何问题,但是在多线程情况下,每个线程都会将i的值加载到自己的本地内存;
比如现在主内存有i=0,线程A与线程B将变量i加载到了自己的工作内存,此时线程A对i加1操作,写回主内存,但是在这时候,线程B感知不到线程A的更改,所以线程B对i加一后,刷新到主内存后,主内存的i还是1;
解决思路:
当CPU在操作Cache中的数据时,如果发现该变量是一个共享变量,也就是说在其他的CPU Cache中也存在一个副本,那么进行如下操作:
Java的内存模型决定了一个线程对共享变量的写入何时对其他线程可见,Java内存模型定义了线程和主内存之间的抽象关系:
假设主内存的共享变量x=0,线程A与线程B分别拥有共享变量x的副本,线程A对x做加1操作后,将x的值刷新到主内存中,当线程B想要使用副本x的时候,就会发现变量已经失效了,必须到主内存中再次获取然后存入自己的工作内存中;
JMM是指 Java Memory Mode指定了Java虚拟机如何与计算机的主内存进行工作;
(1) 原子性
原子性是指在一次的操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行;
(2) 可见性
可见性是指,当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的最新值;
(3) 有序性
所谓有序性是指程序代码在执行过程中的先后顺序,比如java编译器在优化的过程中会发生指令重排序;
(1) JMM与有序性
对基本数据类型的变量读取赋值操作都是原子性的,对引用类型的变量的读取与赋值的操作也是原子性的;
但是,如果是i++这类的操作,它在底层其它是进行了两步操作的,加一和赋值;
(2) JMM与可见性
java提供了以下三种方式来保证可见性:
(3) JMM与有序性
volatile对一个变量的写操作要早于对这个变量之后的读操作;
1.可以保证不同线程之间对共享变量操作时的可见性;
2.可以防止指令重排序;
3.volatile不能保证原子性;
volatile应用最多的地方大概是在DCL+volatile的单例设计上;加了它可以防止指令的重排;
实例化对象的过程:
- 分配内存空间(new)
- 初始化对象(invokespecial)
- 将内存空间的地址赋值给对应的引用(astore_1)
但是由于操作系统可以对指令进行重排序,所以上面的过程也可能会变成如下过程:
- 分配内存空间(new)
- 将内存空间的地址赋值给对应的引用(astore_1)
- 初始化对象(invokespecial)
如果被重排序后成了这样,那么在多线程的情况下,可能会存在正在执行第2步将内存空间的地址赋值给对应的引用后,另外一个线程判断当前线程已经被初始化,直接去使用单例中未被初始化的变量;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。