在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

要解决这个问题,只需要像在本程序中的这样,把该变量声明为volatile(不稳定的)即可,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取。一般说来,多任务环境下各任务间共享的标志都应该加volatile修饰。

Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

用volatile和不用volatile的区别,运行一下,就知道了。

不用volatile:

  1. package com.keyword;
  2. public class TestWithoutVolatile {
  3. private static boolean bChanged;
  4. public static void main(String[] args) throws InterruptedException {
  5. new Thread() {
  6. @Override
  7. public void run() {
  8. for (;;) {
  9. if (bChanged == !bChanged) {
  10. System.out.println("!=");
  11. System.exit(0);
  12. }
  13. }
  14. }
  15. }.start();
  16. Thread.sleep(1);
  17. new Thread() {
  18. @Override
  19. public void run() {
  20. for (;;) {
  21. bChanged = !bChanged;
  22. }
  23. }
  24. }.start();
  25. }
  26. }

运行后,程序进入死循环了,一直在运行。

用volatile:

  1. package com.keyword;
  2. public class TestWithVolatile {
  3. private static volatile boolean bChanged;
  4. public static void main(String[] args) throws InterruptedException {
  5. new Thread() {
  6. @Override
  7. public void run() {
  8. for (;;) {
  9. if (bChanged == !bChanged) {
  10. System.out.println("!=");
  11. System.exit(0);
  12. }
  13. }
  14. }
  15. }.start();
  16. Thread.sleep(1);
  17. new Thread() {
  18. @Override
  19. public void run() {
  20. for (;;) {
  21. bChanged = !bChanged;
  22. }
  23. }
  24. }.start();
  25. }
  26. }

程序输出!=,然后马上退出。

但是,很多情况下,用不用volatile,感觉不出什么区别,什么时候要用volatile呢?看看JDK里使用volatile的类。

比如java.util.regex.Pattern里的变量:

  1. private transient volatile boolean compiled = false;

还有,java.lang.System的变量:

  1. private static volatile Console cons = null;

一般就是初始化的时候,需要用到volatile。

java.util.Scanner里的变量,如:

  1. private static volatile Pattern boolPattern;
  2. private static volatile Pattern separatorPattern;
  3. private static volatile Pattern linePattern;

初始化boolPattern的代码:

  1. private static Pattern boolPattern() {
  2. Pattern bp = boolPattern;
  3. if (bp == null)
  4. boolPattern = bp = Pattern.compile(BOOLEAN_PATTERN,
  5. Pattern.CASE_INSENSITIVE);
  6. return bp;
  7. }

上面的情况,可以使用synchronized来对boolPattern加锁,但是synchronized开销比volatile大,volatile能够胜任上面的工作。

volatile不保证原子操作,所以,很容易读到脏数据。

使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

最新文章

  1. div+css:两个div并排等高 (table-cell)
  2. perl 切换 dnspod 域名记录
  3. Chrome/Firefox 中头toFixed方法四舍五入兼容性问题
  4. C# Socket系列三 socket通信的封包和拆包
  5. 算法训练 Anagrams问题
  6. bzoj1173: [Balkan2007]Point
  7. 系统中使用frameset和Iframe刷新页面session失效
  8. CSS 初体验之一
  9. The 6th Zhejiang Provincial Collegiate Programming Contest->ProblemF:80ers' Memory
  10. 项目检出JRE问题(Unbound classpath container: 'JRE System Library [JavaSE-1.7]' in project 'idweb')
  11. Linux的nginx环境的vue 部署
  12. 数据结构基础——指针及动态内存分配(malloc)
  13. iOS跳转第三方应用举例一号店和京东
  14. Jmter接口网站压力测试工具使用记录
  15. mysql中有多种存储引擎,每种引擎都有自己的特色
  16. A1059. Prime Factors
  17. 进程间通信IPC机制和生产者消费者模型
  18. springboot springcloud 热部署
  19. Flask学习-Flask app接受第一个HTTP请求
  20. ES6 数值的扩展

热门文章

  1. 【Spring学习笔记-5】Spring中的抽象bean以及bean继承
  2. bzoj2565 最长双回文子串
  3. ES之八:elasticsearch2.x下的JAVA API示例
  4. java 调用windows bat脚本
  5. java study文件读写
  6. labview如何生成可执行文件
  7. Web 项目遇到的乱码问题
  8. Spark分析之Master
  9. Python3 os模块应用
  10. mysql 不能插入中文和显示中文