预备知识:
每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图描述这写交互:
在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。
把变量声明为volatile(不稳定的),相当于指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。
volatile不能和final连用:It is a compile-time error if a final
variable is also declared volatile
.
为什么要使用Volatile
Volatile变量修饰符如果使用恰当的话,它比synchronized的使用和执行成本会更低,因为它不会引起线程上下文的切换和调度。
举例:
public class VolatileTest { public static volatile int race = 0; public static void increase() { race++; } public static int THREADS_COUNT = 20; public static void main(String[] args) { Thread[] threads = new Thread[THREADS_COUNT]; for (int i = 0; i < THREADS_COUNT; i++) { threads[i] = new Thread(new Runnable() { public void run() { for (int i = 0; i < 10000; i++) { increase(); } } }); threads[i].start(); } while (Thread.activeCount() > 1) { Thread.yield(); System.out.println(race); } } }
我们的预期是输出200000,但大多数情况下输出的值都比200000小。
观察increase的byte code:
public static void increase(); Code: 0: getstatic #11; //Field race:I 3: iconst_1 4: iadd 5: putstatic #11; //Field race:I 8: return
当getstatic指令把race的值取到操作栈顶时,volatile 关键字保证了race的值在此时是正确的,但是在执行iconst_1,iadd这些指令的时候,其他线程可能已经把race的值加大了。而在操作栈顶的值变成了过期的数据,所以putstatic指令执行后就可能把较小的race值同步到内存中了。
应用volatile变量的四个原则:
(1)写入变量不依赖此变量的值,或者只有一个线程修改此变量
(2)变量的状态不需要与其它变量共同参与不变约束
(3)访问变量不需要加锁
(4)由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
参考:
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
相关推荐
9. **并发编程最佳实践**:最后,书中会提供一些并发编程的最佳实践,帮助读者避免常见的并发陷阱,写出更健壮、更高效的代码。 通过阅读《Java并发编程的艺术》这本书,开发者不仅可以掌握Java并发编程的基础知识...
例如,书中详细介绍了synchronized关键字的作用、 volatile变量的使用、线程池的创建与管理,以及Future接口和ExecutorService在处理异步任务时的应用。 其次,《java并发编程的艺术》是一本针对Java程序员的实用...
本资源"并发编程一之synchronized和volatile.rar"提供了多个示例代码(demo1到demo12),帮助我们深入理解这两个关键字的功能与用法。 1. **synchronized关键字**: - **互斥性**:synchronized可以确保同一时间...
Java并发编程是Java开发者必须掌握的关键技能之一,它涉及到如何在多线程环境中高效、安全地执行程序。并发编程能够充分利用多核处理器的计算能力,提高应用程序的响应速度和整体性能。《Java编程并发实战》这本书是...
源码分析部分则让读者有机会亲手实践,通过阅读和运行书中提供的示例代码,可以更直观地理解并发编程的各种技巧和陷阱。这些源码可以帮助读者在实际项目中应用所学知识,解决实际问题。 在阅读《Java并发编程实战》...
3. **线程安全**:线程安全是并发编程中的重要主题,书中会探讨各种线程安全的实现策略,如使用`synchronized`关键字、`volatile`变量、`final`修饰以及线程局部变量(`ThreadLocal`)。 4. **并发集合**:Java的...
《JAVA并发编程实践》是一本深入探讨Java多线程与并发控制的权威著作。这本书针对Java程序员,旨在帮助他们理解和掌握如何在并发环境中编写高效、可靠的代码。在Java的世界里,并发编程是不可或缺的一部分,特别是在...
《Java并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。并发和多线程是现代计算机系统中不可或缺的部分,特别是在Java这样的多线程支持语言中。本书详细介绍了如何在Java环境中有效地设计和实现...
volatile保证了变量的内存可见性,也就是说,对一个volatile变量的修改,对其他线程立即可见。尽管volatile不能保证操作的原子性,但其在某些场景下能减少锁的使用,从而提高性能。 J.U.C中的并发集合,如...
《Java并发编程实践》这本书是Java开发者深入理解并发编程的重要...通过阅读《Java并发编程实践》,开发者可以学习到如何编写高效、稳定的并发程序,避免并发陷阱,提升软件质量。同时,书中实例丰富,便于实践和理解。
1. **并发基础**:首先,书中会介绍并发编程的基本概念,如线程、进程、同步与通信机制,以及Java中的线程API,如`Thread`类和`Runnable`接口。 2. **线程管理**:讨论如何创建、启动、停止和控制线程,包括线程池...
- **Java Concurrency in Practice**(JCIP):经典的并发编程指南,书中介绍了大量并发编程的最佳实践和陷阱。 - **Akka**和**Quasar**:基于Actor模型和纤程( Fiber)的高性能并发框架,提供更高级别的抽象来...
- **并发编程陷阱与优化技巧**: - 避免过度同步; - 合理使用无锁算法; - 选择合适的并发容器; - 适时采用线程池。 ### 总结 《JAVA并发编程实践》一书深入浅出地介绍了Java并发编程的核心概念和技术细节,...
通过学习《Java并发编程实践》,开发者可以更深入地理解Java并发机制,提高代码的并发性能,避免常见的并发陷阱,从而写出更高效、更可靠的并发程序。这本书是Java并发编程的必读之作,无论你是初学者还是有经验的...
9. **并发编程的最佳实践**:书中总结了一系列的并发编程最佳实践,如最小化锁的粒度、避免过度同步等,帮助开发者编写出更健壮、可维护的并发程序。 通过阅读《Java并发编程实践》,读者不仅可以深入了解Java并发...
最后,作者们分享了一些最佳实践和陷阱,帮助开发者避免常见的并发编程错误。 总的来说,《JAVA并发编程实践》这本书全面覆盖了Java并发编程的各个方面,无论是理论知识还是实战技巧,都是Java开发者不可或缺的参考...
这本书详细介绍了Java并发编程的基础知识,高级技巧以及实战经验,旨在帮助读者掌握如何有效地利用多核处理器的能力,提高程序性能,同时避免并发编程中常见的陷阱。 并发编程是现代软件开发中的重要组成部分,特别...
这本书详细介绍了如何在Java环境中有效地设计和实现并发程序,帮助开发者避免并发编程中常见的陷阱。 首先,书中会讲解Java并发编程的基础概念,包括线程、进程、同步和异步的概念。线程是操作系统分配CPU时间的...