JAVA中的volatile

一、Java并发中的3个概念:
1.原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。举个最简单的例子,大家想一下假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果?

x = 10;         //语句1
y = x;         //语句2
x++;           //语句3
x = x + 1;     //语句4

语句1是直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。
语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。
同样的,x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。
所以上面4个语句只有语句1的操作具备原子性。
  
2.可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。“可见性”并非是说修改了之后,会”通知”其他线程去取最新的数据,而是下次有线程来取这个数据的时候,禁止他们从自己的线程缓存中取数据,直接到原始的内存去取最新的值。
例子:

//线程1执行的代码
int i = 0;
i = 10;

//线程2执行的代码
j = i;

假若执行线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为10,那么在CPU1的高速缓存当中i的值变为10了,却没有立即写入到主存当中。此时线程2执行 j = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得j的值为0,而不是10.这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。

3.有序性:即程序执行的顺序按照代码的先后顺序执行。

4.要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

二、volatile关键字
1.执行结果非常可能不是1000000,例如在我的电脑上执行结果为40多万。

public class Test2 {
     public volatile int count = 0;

        public void increase() {
            count++;
        }

        public static void main(String[] args) {
            final Test2 test = new Test2();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<100000;j++)
                            test.increase();
                    };
                }.start();
            }

            while(Thread.activeCount()>1)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.count);
        }
}

2.原因:volatile 只能保证 “可见性”,不能保证 “原子性”。

  • count++; 这条语句由3条指令组成:
    (1)将 count 的值从内存加载到 cpu 的某个寄存器r
    (2)将 寄存器r 的值 +1,结果存放在 寄存器s
    (3)将 寄存器s 中的值写回内存

  • 在没有 volatile 的时候,执行完 count++;,执行结果其实是写到CPU缓存中,没有马上写回到内存中,后续在某些情况下(比如CPU缓存不够用)再将CPU缓存中的值flush到内存。正因为没有马上写到内存,所以不能保证其它线程可以及时见到执行的结果。

  • 在有 volatile 的时候,执行完 count++;,执行结果写到CPU缓存中,并且同时写回到内存,因为已经写回内存了,所以可以保证其它线程马上看到执行的结果。
  • 可能有两个线程同时执行(1),所以(2)计算出来一样的结果,然后(3)存回的也是同一个值。
  • 如果线程1已经读取了count的值,还没有进行++操作,这个时候线程2读取了内存中的count值,这时候线程1执行count++,接着线程2再执行count++。 在线程1执行完count++之后,线程2执行count++之前,线程2还会去内存中读取count的值吗?不会,所以这就导致了异步的情况。

来源:
java中关于volatile的理解疑问 回答1
java中关于volatile的理解疑问 回答2
Java并发编程:volatile关键字解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值