网站首页 > java教程 正文
这几年java面试,稍微去技术好点的公司或项目组要求技术高点的,对java的并发相关的面试是必问的。
今天我们主要来说说最常问的及经常用和面试问的频率比较高的一个关键字volatile的原理,包括我也经常做为面试官,问到volatile频率还是比较高的,具体还是根据项目对开发人的要求吧。但当问到volatile几乎所有人就是说的很模糊、东一句、西一句。最后我也不知道他们在说什么,甚至有的人都没听说过volatile关键字,这篇文章大概把volatile原理说一下,这样能帮助好多小伙伴对volatile有所了解,以后出去面试,能轻松应付。
我们先看一段代码,先热热身,看看能不能发现一些问题
public class volatileTest {
public static int count = 0;
public static void main(String[] args) {
new Thread() {
public void run(){
int countNum = count;
while (true) {
if (countNum != count) {
System.out.println("修改前的值:"+ count);
countNum = count;
}
}
}
}.start();
new Thread(){
public void run() {
int countNum = count;
while(true) {
System.out.println("修改后的值为:"+ ++countNum);
count = countNum;
try {
TimeUnit.SECONDS.sleep(2);
}catch (Exception e) {
e.printStackTrace();;
}
}
}
}.start();
}
}
运行结果为:
修改后的值为:1、修改后的值为:2、修改后的值为:3.....
只要开启多个线程,一定会有一些这种问题,一个线程修改了变量的值,另一个线程要立马知道这个值被另一个线程给修改了,如果你不用volatile,其它线程则感知不到某个变量的值被修改了。加上volatile的关键字以后,二个线程都能发现,并同时对他进行+1操作
public volatile static int count = 0;
运行结果为:
修改后的值为:1
修改前的值:1
修改后的值为:2
修改前的值:2
修改后的值为:3
修改前的值:3
修改后的值为:4
修改前的值:4
我们先看一下volatile内存模型,有一个公共内存和一个工作内存组成,如下图:
下面我们通过图来一步一步的讲一下volatile的原理
如果两个线程,他们都需要读取count这个值,他们都要从主内存里读取count的值到自己的工作内存中,然后才使用这个值。
线程A和线程B都从各息的工作内存中读取值,现在线程A和线程B都读取到了这个值count =0
那么为什么非要加载到工作内存中了,其实有经验的小伙伴,已经知道为什么了,这个其实就相当于我们写程序使用redis缓存一样,就是为了提高性能。
虽然提高了性能,但又会带来一个新的问题,就是线程A修改了count的值为1,同时将这个值写入自己的工作内存中,此时线程A的工作内存里的count值为1
而此时主内存里的count值还是为0,可这时线程B里的工作内存count值还是0,
而现在出现一个情况是
线程A只对自己工作内存进行+1操作
线程B只对自己工作内存进行+1操作
两个线程互相感知不到,而我们需要的结果是不管线程A、线程B对同一个变量加+1操作,都是在count最新的值进行+1的,这才是我们想要的结果。
这个问题就是多个线程对同一个变量操作的可见性问题,多个线程并发读写一个共享变量的时候,一个线程修改了值,其它线程要立刻感知到最新的值。
那如何让程序,按照我们的思路执行了,这时就要用到volatile,只要定义变量里加上volatile,就可以按照我们的想法执行了。
如果在定义变量时加上volatile,线程A只要修改count的值,就会在修改自己的工作内存count值之后,强制将count的最新值刷入主内存。
刷回主内存以后,这时还有一个步骤就是,将其它所有线程的工作内存过期掉,就想当于我们写程序时redis缓存过期一个道理。
如果这时线程B对count操作,发现自己工作内存的值已经被过期了,无法使用了,这时会向工作内存中获取count的最新的值
这样volatile就解决取可见性的问题,但这里有一个问题,如果多个线程同时对一个变量进行修改,可能会造成数据错乱,volatile是无法保证原子性的,原子性还得依靠锁进行解决,以后有机会再以单独文章进行讲解
猜你喜欢
- 2024-11-17 管理Java依赖的最佳实践(java依赖管理工具)
- 2024-11-17 程序员的福音 - Apache Commons VFS(上)
- 2024-11-17 程序员的福音 - Apache Commons Pool
- 2024-11-17 Java 动态代理,来自一个菜鸟的一点实践与思考
- 2024-11-17 程序员的福音 - Apache Commons HttpClient
- 2024-11-17 程序员的福音 - Apache Commons IO
- 2024-11-17 Java程序员面试中,2分钟鉴定你是不是菜鸟程序员
- 2024-11-17 程序员的福音 - Apache Commons Codec
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)