`

Java中volatile如何保证long和double的原子性操作

 
阅读更多

原创转载请注明出处:http://agilestyle.iteye.com/blog/2359969

 

关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对于多个线程访问同一个实例变量需要加锁进行同步。

package org.fool.java.concurrent.volatiletest;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VolatileTest1 {

    private static volatile int count = 0;

    private static void addCount() {
        for (int i = 0; i < 100; i++) {
            count++;
        }

        System.out.println(Thread.currentThread().getName() + " count = " + count);
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    VolatileTest1.addCount();
                }
            });
        }

        executor.shutdown();
    }
}

Note:

addCount()方法没有加synchronized

Console Output


预期结果应该是10000,尽管count被volatile修饰,保证了可见性,但是count++并不是一个原子性操作,它被拆分为load、use、assign三步,而这三步在多线程环境中,use和assgin是多次出现的,但这操作是非原子性的,也就是在read和load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公有内存中的变量不同步,所以计算出来的值和预期不一样,就产生了线程安全的问题,所以需要用synchronized进行加锁同步

 

addCount()方法用synchronized进行加锁同步

Console Output 


结果10000与预期一致 

 

所以volatile只能保证可见性不能保证原子性,但用volatile修饰long和double可以保证其操作原子性。


所以从Oracle Java Spec里面可以看到:

  • 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。
  • 如果使用volatile修饰long和double,那么其读写都是原子操作
  • 对于64位的引用地址的读写,都是原子操作
  • 在实现JVM时,可以自由选择是否把读写long和double作为原子操作
  • 推荐JVM实现为原子操作 

Reference

http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

http://www.cnblogs.com/louiswong/p/5951895.html

http://ifeve.com/volatile/

http://www.infoq.com/cn/articles/java-memory-model-4/

 

  • 大小: 66.7 KB
  • 大小: 13.2 KB
  • 大小: 22.5 KB
  • 大小: 11.1 KB
  • 大小: 22 KB
分享到:
评论

相关推荐

    java里的volatile关键字详解

    Java中的Volatile关键字详解是Java中的一种关键字,用于保证线程之间的可见性、原子性和有序性。下面是对Java中的Volatile关键字详解的知识点总结: 一、基本概念 1. 可见性:可见性是一种复杂的属性,因为可见性...

    java 并发操作之原子性与可视性1

    例如,简单的赋值操作`a=0`对于非`long`和`double`类型的变量是原子性的,因为这个操作是不可分的。然而,像`a++`这样的操作实际上包含多个步骤:读取当前值,增加1,然后写回新值,因此它不是原子性的。非原子操作...

    java 中volatile和lock原理分析

    3. 原子性:尽管volatile关键字能保证可见性和有序性,但它并不能保证对64位变量(如long和double)的原子性操作。这意味着对于i++这样的操作,volatile并不能防止并发问题。 然而,volatile并不是万能的。它无法...

    Java试题笔试题目答案.pdf

    但volatile修饰的long和double变量的读写操作是原子的,这意味着在多线程环境下,它们的读取和写入会被保证为一次性完成,避免了数据不一致的问题。 3) volatile的一个实践场景是用于修饰long和double变量,以确保...

    Java程序员面试题集锦-java程序员面试试题与解析doc精品模板.docx

    但volatile修饰的long或double变量的读写操作是原子的,确保了多线程环境下的正确性。 3) **Volatile实践** 一个实践场景是使用volatile修饰long和double变量,以实现原子性读写。此外,volatile还提供了内存屏障...

    Java 80道面试题和答案.docx

    3. **原子性**:虽然volatile不能保证所有的操作都是原子性的,但对于64位的数据类型如long和double,volatile提供了原子性的读写操作。这意味着,尽管Java中普通long和double的读写不是原子的,但volatile修饰的...

    Java 80 道面试题及答案.docx

    例如,对于64位的数据类型如long和double,非原子操作通常需要两次读取或写入,但在volatile变量上,这些操作被视为原子性的,保证了多线程环境下的完整性。 4. 实践应用: - volatile修饰long和double变量,使其...

    Java并发三大性质.docx

    在Java中,不是所有操作都具有原子性,例如,简单的赋值操作`int a = 10`是原子的,但自增操作`a++`则不是,因为它涉及读取、计算和写回三个步骤。 Java内存模型(JMM)定义了8种原子操作:lock(锁定)、unlock...

    Volatile的正确使用1

    Java中的`volatile`关键字是用来解决多线程环境下的可见性和有序性问题的,但它并不提供原子性保证。这里我们深入探讨一下`volatile`的正确使用和限制。 首先,`volatile`变量确保了线程间的可见性,即一旦一个线程...

    java2015后出现的高级面试题

    - **原子读写**:对于`long`和`double`类型的数据,使用`volatile`可以确保其读写操作的原子性。 - **提供内存屏障**:`volatile`可以插入内存屏障来确保写入操作对所有线程可见,同时确保写入操作前后其他数据的...

    Java面试题及答案-共80道.docx

    volatile关键字在Java中扮演着重要的角色,确保了多线程环境中的可见性和有序性。 首先,我们可以明确Java确实允许创建volatile数组。但是,volatile只保证了数组引用的可见性,即当数组引用发生改变时,其他线程...

    79个Java面试题总结.docx

    然而,对于long和double这样的64位类型,volatile确保它们的读写操作是原子性的。这是因为Java默认对long和double的读取不是原子的,而volatile修正了这个问题。 3)volatile实践的一个例子是在分布式框架中作为...

    80道Java面试题及答案

    volatile 在 Java 中用于标记变量,确保其在多线程环境中的可见性和有序性,但不保证原子性。以下是对 volatile 关键字及其相关知识点的详细解释: 1. **volatile 变量的可见性**: - 当一个线程修改了 volatile ...

Global site tag (gtag.js) - Google Analytics