首页 > 针对volatile的解决方法?

针对volatile的解决方法?

package basic;

public class TestVolatile {

    public volatile static int count = 0;

    public static void increase() {
        try {
            // 延迟10毫秒,使得结果明显
            Thread.sleep(10);
            count++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    TestVolatile.increase();
                }
            }).start();
        }
        System.out.println("期望运行结果:10000");
        System.out.println("实际运行结果:" + TestVolatile.count);
    }
}

代码如下,由于volatile的read and load操作并不是原子性的,它只保证了从主内存读到栈内存的值是最新的,因此上述代码运行结果并不是语气的10000。
问题:
1.既然volatile并没有保证原子性,为什么还要使用它?
2.JDK中针对volatile的这种弊端,还做了哪些设计来弥补这一缺憾?


在任务的执行过程中,为了提高效率,有时会重排序初始化的过程,这在多线程并发的情况下或许会让程序不稳定,用volatile可以阻止重排序。

memory = allocate();   //1:分配对象的内存空间
ctorInstance(memory);  //2:初始化对象
instance = memory;     //3:设置instance指向刚分配的内存地址

上面三行伪代码中的2和3之间,可能会被重排序(在一些JIT编译器上,这种重排序是真实发生的,详情见参考文献1的“Out-of-order writes”部分)。2和3之间重排序之后的执行时序如下:

memory = allocate();   //1:分配对象的内存空间
instance = memory;     //3:设置instance指向刚分配的内存地址
                       //注意,此时对象还没有被初始化!
ctorInstance(memory);  //2:初始化对象

此时若有多线程并发访问,就有可能会出现对象还未初始化的情况。
而volatile关键字,会阻止这种优化,保证每次都会按照第一段代码的顺序执行。
来源:http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization


volatile的大致作用@FreeBirdLjj已经总结性的说了下,补充几点

  1. volatile作用是保证了变量在不同线程之间的内存可见性和对单个volatile变量的读写的原子性,主要手段是在编译器生成指令序列时,插入特定类型的内存屏障来实现的

  2. volatile内存的可见性,主要通过禁止重排序(编译器重排序,处理器重排序),保证缓存及时失效,保证缓存及时刷入主内存来实现的

  3. 对单个volatile变量的读取和设值语义等价与
    public synchronized A getA();
    public synchronized void setA();


volatile可以避免寄存器优化,而且在 Java 里还自带 barrier,虽然不能防止竞争,但可以用于跨线程的无竞争的同步。
如果数据还有竞争的话,就要考虑 Atomic 的数据类型了。不过最好尽量避免竞争,因为竞争会降低并发度。


你需要的是【锁】。

【热门文章】
【热门文章】