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,这里不讨论。
三 Atomicity和volatility 的区别
二者是完全不同的概念:
原子操作是指不可中断,是指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了。
- java volatile 探究
- Java多线程探究-关键字volatile解析
- 线程安全与并发编程探究(七)--volatile java内存模型及线程知识小结
- 探究java多线程中正确的单例模式 volatile关键字
- 探究java多线程中正确的单例模式 volatile关键字
- 探究java多线程中正确的单例模式 volatile关键字
- java-volatile
- java volatile
- volatile java
- java volatile
- Java volatile
- volatile Java
- java volatile
- java volatile
- java volatile
- java volatile
- Java volatile
- Java volatile
- 冒泡排序
- spring 在Thread中注入@Resource@Autowired失败,总为null~解决
- 规则引擎笔记
- android apk 防止反编译技术第一篇-加壳技术
- navicat for oracle 表数据导入乱码问题
- java volatile 探究
- Apache Lucene初探
- 数据库关于group by 两个或以上条件的分析
- C++重要性质03:多态(Polymorphism)
- IaaS, PaaS, SaaS的区别
- C语言学习笔记(1)
- Hadoop集群性能测试
- main函数外也可以调用函数
- Getting Started with 'nanomsg'