java volatile 探究

来源:互联网 发布:安徽快三遗漏数据统计 编辑:程序博客网 时间:2024/05/16 04:41

一 同步问题

先看一个多线程的例子,地球人都知道,这样的代码一定会有问题

public class Test {static int x = 0;private static int thread_num = 1000;public static void add (){x++;}public static void main(String[] args) {Thread [] task = new Thread[thread_num];Thread addThread = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubadd();}});// 多个线程调用addfor (int i =0;i < thread_num;i++){task[i]=new Thread(addThread);task[i].start();}// 等待所有线程执行完毕for (int i=0;i<thread_num;i++){try {task[i].join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println("x is "+x);}}

于是有人就说使用volatile修饰一下x,好的,我们用volatile修饰一下,代码如下:

public class Test {static volatile int x = 0;private static int thread_num = 1000;public static void add (){x++;}public static void main(String[] args) {Thread [] task = new Thread[thread_num];Thread addThread = new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubadd();}});// 多个线程调用addfor (int i =0;i < thread_num;i++){task[i]=new Thread(addThread);task[i].start();}// 等待所有线程执行完毕for (int i=0;i<thread_num;i++){try {task[i].join();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println("x is "+x);}}

执行结果:

x is 998


二 volatile 与atomic的机制

加了volatile为什么还会出错呢?我们得从原子性和volatility说起。

 

Atomic operation:原子性是指在线程调度中不可以被中断的操作。

 

volatility: 对于多核的处理器,每个核都有自己的cache,因此每个核的变量对其他核是不可见(invisible)的,只有写到主存里才是所有核都可见的。而volatility就是一种保障多处理器间visibility的机制。如果将一个域声明为volatile,在jvm执行时会让处理器直接从主存(main memory)中读取数据(如果非volatile则会从cache中读取),写数据的时候也会直接写入主存。另外,多线程和多核cpu也不是一个概念,每个cpu都有自己的cache,每个线程也有自己的local storage,这里不讨论。

 

三 Atomicityvolatility 的区别

 

 二者是完全不同的概念:

原子操作是指不可中断,是指cpu将数据写入cache或从cache时读取时的不可中断。

对于一个非volatile域,对其的原子操作不需要立即写入主存,因此其他的线程也不一定能看到最新的值。

java中,原子操作和volatile没关系,两者一个是说线程调度不中断,一个是直接读写主存。


四 volatile不能保证同步问题所在

上述代码中volatile不能保证同步的原因是x++不是原子操作,在JVM的编译执行中最后是分成多步的,这里不对x++进行具体分析,我们将这么一个操作抽象成3步:

1 读取x

2 x加1

3 将x写回

由于volatile的修饰,因此我们对x的读写都是直接操作主存的,假设有两个线程并行操作,我们用三幅图表示,数字代表指令执行顺序,可能会出现如下情况:

                                                                                          图1


                                                                                             图2


                                                                                              图3

1中两个线程或同时或先后读取x=0

2中两个线程完成加1

3中两个线程先后写如x=1,预期结果应该是2,出错!


由于volatile的修饰,因此我们对x的读写都是直接操作主存的,假设有两个线程并行操作,可能会出现如下情况:

由上图知道,虽然volatile对主存进行操作,由于x++的非原子性,因此会导致最后结果的出错。

线程同步时,多个线程对变量访问时,volatile并不能保证同步,想要保证正确性,需要让线程使用synchronized同步。Synchronization机制保障将数据flush入主存中,因此用synchronization机制就不需要再声明volatile了。 


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 15个月的小孩长泡疹怎么办 墨盒加错颜色墨水怎么办 墨盒颜色加错了怎么办 uV打印有色差是怎么办 cmyk和rgb有色差怎么办 染头发新长出来怎么办 白色衣服弄上口红怎么办 口红弄到牛仔裤上怎么办 口红弄到沙发上怎么办 做了拔罐后背疼怎么办 拔罐拔出血水后怎么办 拨了罐浑身疼怎么办 拔完火罐洗澡疼怎么办 拔罐之后脊背疼怎么办? 五个月的狗胆小怎么办 电脑颜色不正常分辨率调不了怎么办 宝宝认人怎么办 五个月 新生儿42天听力筛查未通过怎么办 幼儿大便干燥拉不下粑粑怎么办 宝宝大便干燥拉粑粑带浓血怎么办 新生儿便秘4天了怎么办 写卷子的题目不认识怎么办? 2个月小猫尿床怎么办 安全教案鱼刺卡住了怎么办 被鱼刺卡住怎么办安全教案 小班健康教案生病了怎么办 被蜜蜂蛰了怎么办教案 走丢了怎么办大班反思 小孩吃了硬物怎么办 小孩吞了硬物怎么办 小学二年级孩子成绩不好怎么办 二年级孩子成绩不好怎么办 一年级学的不好二年级怎么办 17个月宝宝夜奶怎么办 小孩吃积食了吐怎么办 11个宝宝不会爬怎么办 5岁宝宝严重挑食怎么办 孩子吃菜口味重怎么办? 孩子挑食不吃菜不吃肉怎么办 微信新的朋友回复频繁怎么办 忘了闺蜜生日怎么办