原创转载请注明出处: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://www.infoq.com/cn/articles/java-memory-model-4/
相关推荐
Java中的Volatile关键字详解是Java中的一种关键字,用于保证线程之间的可见性、原子性和有序性。下面是对Java中的Volatile关键字详解的知识点总结: 一、基本概念 1. 可见性:可见性是一种复杂的属性,因为可见性...
例如,简单的赋值操作`a=0`对于非`long`和`double`类型的变量是原子性的,因为这个操作是不可分的。然而,像`a++`这样的操作实际上包含多个步骤:读取当前值,增加1,然后写回新值,因此它不是原子性的。非原子操作...
3. 原子性:尽管volatile关键字能保证可见性和有序性,但它并不能保证对64位变量(如long和double)的原子性操作。这意味着对于i++这样的操作,volatile并不能防止并发问题。 然而,volatile并不是万能的。它无法...
但volatile修饰的long和double变量的读写操作是原子的,这意味着在多线程环境下,它们的读取和写入会被保证为一次性完成,避免了数据不一致的问题。 3) volatile的一个实践场景是用于修饰long和double变量,以确保...
但volatile修饰的long或double变量的读写操作是原子的,确保了多线程环境下的正确性。 3) **Volatile实践** 一个实践场景是使用volatile修饰long和double变量,以实现原子性读写。此外,volatile还提供了内存屏障...
3. **原子性**:虽然volatile不能保证所有的操作都是原子性的,但对于64位的数据类型如long和double,volatile提供了原子性的读写操作。这意味着,尽管Java中普通long和double的读写不是原子的,但volatile修饰的...
例如,对于64位的数据类型如long和double,非原子操作通常需要两次读取或写入,但在volatile变量上,这些操作被视为原子性的,保证了多线程环境下的完整性。 4. 实践应用: - volatile修饰long和double变量,使其...
在Java中,不是所有操作都具有原子性,例如,简单的赋值操作`int a = 10`是原子的,但自增操作`a++`则不是,因为它涉及读取、计算和写回三个步骤。 Java内存模型(JMM)定义了8种原子操作:lock(锁定)、unlock...
Java中的`volatile`关键字是用来解决多线程环境下的可见性和有序性问题的,但它并不提供原子性保证。这里我们深入探讨一下`volatile`的正确使用和限制。 首先,`volatile`变量确保了线程间的可见性,即一旦一个线程...
- **原子读写**:对于`long`和`double`类型的数据,使用`volatile`可以确保其读写操作的原子性。 - **提供内存屏障**:`volatile`可以插入内存屏障来确保写入操作对所有线程可见,同时确保写入操作前后其他数据的...
volatile关键字在Java中扮演着重要的角色,确保了多线程环境中的可见性和有序性。 首先,我们可以明确Java确实允许创建volatile数组。但是,volatile只保证了数组引用的可见性,即当数组引用发生改变时,其他线程...
然而,对于long和double这样的64位类型,volatile确保它们的读写操作是原子性的。这是因为Java默认对long和double的读取不是原子的,而volatile修正了这个问题。 3)volatile实践的一个例子是在分布式框架中作为...
volatile 在 Java 中用于标记变量,确保其在多线程环境中的可见性和有序性,但不保证原子性。以下是对 volatile 关键字及其相关知识点的详细解释: 1. **volatile 变量的可见性**: - 当一个线程修改了 volatile ...