Java内存模型与线程(三):volatile型变量的规则

volatile变量一般是比较难以理解,这里坐着希望列举几个小例子让大家充分理解它的使用方法。

对于定义了volatile的变量,这里有两条特性需要大家记住:

第一、保证此变量对所有的线程都是可见性,这里的可见行是指一个线程修改了这个变量的值,新值对于其他的线程来说是可知的。这里,volatile变量不存在一致性问题,volatile变量在并发情况下并非安全。这通过一段程序说明:

package cn.edu.hust.jvm;

public class MainTest {


    public static volatile int value=0;

    public static void incr()
    {
        value++;
    }

    public static void main(String[] args)
    {
        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++)
        {
            threads[i]=new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j=0;j<1000;j++)
                    {
                        incr();
                    }
                }
            });
            threads[i].start();
        }
        System.out.println(value);
    }
}

如果volatile变量时并发安全的话,那么输出结果应该是10000,这里的实际输出结果是


可以看出volatile变量是线程不安全的。那么这是为什么呢?

这里主要的原因是value++这一行代码中,执行这一行代码前,每一个线程的工作空间需要读取value的值大小,这一时刻每一个线程的value值都是一样的的,但是在执行自增操作的时候,可能有些线程执行比较快,有些执行的比较慢,当慢的执行操作时,可能value的值改变。

所以我们在使用volatile关键值时,也需要使用synchronized关键值活着并发包下的类来保证原子操作。

第二、禁止指令重排序优化。这里的重排序,指的是在正确结果的前提下,或者是保证依赖关系的前提下,cpu采用多条指令不按照程序顺序执行分发给各个电路单元处理。

对于上一节的8种规则,volatile关键字有如下的规则:

有load操作才有use工作,反过来也成立,这里必须连续一起出现,这保证类线程从主内存获取最新值,也就是volatile的可见行

有assign动作才有store动作,反过来也成立,这里必须连续一起出现。

还有最后一条是动作执行的先后顺序。这保证了禁止指令重新排序问题。

猜你喜欢

转载自blog.csdn.net/oeljeklaus/article/details/80143538