What is volatile?

一次偶然的机会(java多线程电梯作业寻求多个进程分享变量的方法),接触到了volatile,因此我查阅了相关的材料,对这部分做了一些了解,在这里和大家分享一下。

首先,我们先来聊一聊几个概念

1、What is reorder

编译器和JVM通过改变程序的处理顺序来优化程序,用于提高程序性能的方式。

多线程程序设计中,重排序会导致运行错误。

举个栗子:

class Compare {
   private int x = 0;
   private int y = 0;
   
   public void write() {
       x = 100;
       y = 50;
  }
   
   public boolean compare() {
       return x<y
  }
}
​
public class Main {
   public static void main(String[] args) {
       final Compare com = new Compare();
       new Thread() {
           public void run() {
               com.write();
          }
      }.start();
       new Thread() {
           public void run() {
               com.read();
          }
      }.start();
  }
}

让人吃惊的是,x<y居然真的存在true的情况

原因就在于重排序

编译器的优化策略可能会改变x,y的赋值顺序,导致x<y

显然,我们可以通过synchronized来解决这个问题

2、What is visibility

线程A将某个值写入字段x,线程B读到了这个值

多线程中的可见性问题来源于normal read/write操作是通过缓存在执行的,read到的不一定是最新值,write的也不一定立即对其他线程可见

而synchronized是解决这一问题的有效方法,相信大家也并不陌生,具体用法可以参考我的另一篇博客

其实,volatile也是一种不错的方法

3、What is atomicity

不可分割的操作,例如某线程正在执行synchronized方法,其他线程无法进入该方法,从多线程的角度,这就是原子操作。

Java定义了一些原子操作:primitive type(char、int)的赋值和引用,对象等引用类型的赋值和引用

但是long与double的操作不是原子的,在线程共享时需要放入synchronized

now,进入正题

Volatile

Make sure that a given variable is read directly from main memory and always written back to main memory when updated

volatile具有同步处理(参考sunchronization)和对long和double的原子操作这两种功能

同步处理

1、如果线程A向volatile字段写入的值对线程B可见,那么之前向其他字段写入的值都是对B可见

2、向volatile字段读取和写入前后不会发生重排序

看到这里,我们发现重排序和可见性的问题好像都被volatile解决了

在这里,我们来看一段代码,深入理解一下同步处理

class TryVolatile {
   private int num = 0;
   private volatile boolean valid = false;
   
   public void write() {
       num = 1;
       valid = true;
       /*
        *1、不会被重排序
        *2、线程B中valid会为true
        *3、线程B可能出现num=1
        */
  }
   
   public void read() {
       if (valid) {
           System.out.println(this.num);
      }
  }
}
​
public class Main {
   public static void main(String[] args) {
       final tryVolatile = new TryVolatile;
       new Thread() {
           public void run() {
               tryVolatile.write();
          }
      }.start();
       new Thread() {
           public void run() {
               tryVolatie.read();
          }
      }.start();
  }
}

总结一下volatile的使用:

1、volatile字段赋值语句位置很重要!!!(可以运行上面的代码观察

2、volatile不会进行线程互斥处理(volatile字段不会进入等待队列

3、访问volatile字段会产生性能开销(参考synchronized

对long和double的原子操作

tip:

java.util.concurrent.atomic包提供了原子操作编程的类,例如AtomicInteger、AtomicLong、AtomicIntegerArray等,都是通过封装volatile得到的

What is volatile?的更多相关文章

  1. 多线程同步工具——volatile变量

    关于volatile,找了一堆资料看,看完后想找一个方法去做测试,测了很久,感觉跟没有一样. 这本书<深入理解Java内存模型>,对volatile描述中有这样一个比喻的说法,如下代码所示 ...

  2. Java并发编程:volatile关键字解析

    Java并发编程:volatile关键字解析 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在 ...

  3. 多线程中的volatile和伪共享

      伪共享 false sharing,顾名思义,“伪共享”就是“其实不是共享”.那什么是“共享”?多CPU同时访问同一块内存区域就是“共享”,就会产生冲突,需要控制协议来协调访问.会引起“共享”的最 ...

  4. volatile

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

  5. c#下volatile关键字

      volatile多用于多线程的环境,当一个变量定义为volatile时,读取这个变量的值时候每次都是从momery里面读取而不是从cache读.这样做是为了保证读取该变量的信息都是最新的,而无论其 ...

  6. Java并发之原子变量和原子引用与volatile

    我们知道在并发编程中,多个线程共享某个变量或者对象时,必须要进行同步.同步的包含两层作用:1)互斥访问(原子性):2)可见性:也就是多个线程对共享的变量互斥地访问,同时线程对共享变量的修改必须对其他线 ...

  7. 单例模式中用volatile和synchronized来满足双重检查锁机制

    背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...

  8. volatile修饰符

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

  9. Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

  10. java中关键字volatile的作用

    用在多线程,同步变量. 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B.只在某些动作时才进行A和B的同步.因此存在A和B不一致的情况.volatile就是用来 ...

随机推荐

  1. python3排序 sorted(key=lambda)

    使用python对列表(list)进行排序,说简单也简单,说复杂也复杂,我一开始学的时候也搞不懂在说什么,只能搜索一些英文文章看看讲解,现在积累了一些经验,写在这里跟大家分享,我们通过例子来详细解释一 ...

  2. C#学习笔记——MDI窗体(多文档界面)

    1.设置父窗体: 如果要将某个窗体设置为父窗体,只需将该窗体的IsMdiContainer属性设置为True即可. 2.设置子窗体: 通过设为某个窗体的MdiParent属性来确定该窗体是那个窗体的子 ...

  3. SQL Server 数据库备份和还原

    一.SQL命令 备份BACKUP DATABASE TestDb TO DISK='d:\TestDb.bak'还原RESTORE DATABASE TestDb FROM DISK='d:\Test ...

  4. FATAL ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)”

    https://stackoverflow.com/questions/29188789/eclipse-mac-os-x-debug-error-fatal-error-in-native-meth ...

  5. PostMan如何做Post请求测试

    首先要下载 一个Postman的软件,我这里没有下载地址,据说要翻 墙 下面是使用postman模拟post请求的步骤 我这里请求的API地址和请求的参数都是乱填写的,使用的时候请自行替换你们需要的A ...

  6. Lesson 3-1(语句:条件语句)

    3.1 条件语句:if 语句 3.1.1 if 语句组成 --- if 语句包含:if 关键字.条件.冒号.if 子句(缩进代码块). --- if 语句表达的意思为:如果条件为真(True),执行后 ...

  7. 《剑指offer》二叉树的深度

    本题来自<剑指offer> 反转链表 题目: 思路: C++ Code: Python Code: 总结:

  8. Linux-进程描述(1)—进程控制块

    进程概念介绍 进程是操作系统对运行程序的一种抽象. • 一个正在执行的程序: • 一个正在计算机上执行的程序实例: • 能分配给处理器并由处理器执行的实体: • 一个具有普以下特征的活动单元:一组指令 ...

  9. VMware启动时提示我已移动或我已复制该虚拟机

    参考地址:https://blog.csdn.net/luxiangzhou/article/details/79626113 1.VMware启动时提示“我已移动该虚拟机”或“我已复制该虚拟机”,选 ...

  10. docker容器时间与宿主机时间不一致问题

    该问题是宿主机和容器时去不一致导致的. 把本机时区复制到宿主机即可: docker cp /etc/localtime a9c27487faf4:/etc/localtime 然后重启容器.