前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >volatile的使用与原子性问题

volatile的使用与原子性问题

作者头像
keithl
发布2020-03-10 15:29:39
5320
发布2020-03-10 15:29:39
举报
文章被收录于专栏:疾风先生疾风先生
1. volatile的使用

java源代码

代码语言:javascript
复制
public class VolatileUsedClass {

    private static int sharedVar = 10;

    public static void main(String[] args) throws Exception {

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // modify the sharedVar,write first
                    TimeUnit.MICROSECONDS.sleep(500L);
                    sharedVar = 20;
                    System.out.printf("%s modify the shared var to %s  ...\n", "thread-1", sharedVar);
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // read the value,read for the last
                    TimeUnit.MICROSECONDS.sleep(505L);
                    System.out.printf("%s read the shared var %s \n", "thread-2", sharedVar);
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        });

        thread2.start();
        thread1.start();

        thread1.join();
        thread2.join();

        System.out.println("finish the thread task...");
    }
}

客户端模式-client

  • 不加volatile的执行结果(多次执行)
  • 加volatile的执行结果(多次)
  • 结果分析
    • 在源代码中是添加时间休眠主要是保证先写后读的逻辑
    • 从运行结果可以看出,虽然时间片很短,读线程的数据仍然是本地缓存的数据,并没有从主内存中读取值
    • 添加volatile关键字之后,可以看到读线程的数据正是写线程之后的数据,也就是写读数据是一致的

服务端模式,-server

  • 无volatile修饰的执行结果(多次)
  • 有volatile修饰的执行结果(多次)
  • 结果分析
    • 不带volatile修饰与客户端执行效果一致
    • 但是使用server模式带有volatile的方式却出现了数据不一致的情况,为什么?
    • 原因在于-server模式会在编译成字节码的时候进行代码重排序导致的,主要用于优化程序提升执行效率

小结

  • clinet模式是jdk执行的默认配置,可用于测试环境或者本地开发
  • server模式一般用于生产环境,目的是开启server模式的时候编译器会针对OS系统情况做一些优化操作
  • 服务端与客户端运行模式参考:
代码语言:javascript
复制
参考JVM Server vs Client Mode:
https://javapapers.com/core-java/jvm-server-vs-client-mode/
2. 原子性问题

说明: 以下运行环境是使用-client模式进行,排除重排序的干扰

Java中的原子性

  • jdk文档对于原子性的说明如下:
    • 除了long和double类型之外,引用变量与大多数的原始数据类型都具备读写操作的原子性
    • 所有使用volatile修饰的变量都具备读写操作的原子性
  • 分析
    • 针对64bit的数据类型,主要与处理器(32bit/64bit)有关,在32bit处理器上,JVM会将64bit的long/double划分为两个32bit的写操作,并不具备原子性(数据的读写主要是通过处理器总线与主内存进行传递)
    • 基于Happen-Before原则,对于volatile的变量读取总是可以“看到”任何一个线程对该volatile变量的最后写入,因此在临界区代码的执行是具备原子性,即使是long或是double类型

volatile修饰单个变量的自增减问题

  • 代码
代码语言:javascript
复制
// 部分代码,在上述的写线程进行修改, 前提: volatile修饰变量sharedVar
Thread t1 = new Thread(){
            @Override
            public void run() {
                try {
                    // modify the sharedVar
                    TimeUnit.MICROSECONDS.sleep(500L);
                    sharedVar ++;
                    System.out.printf("%s modify the shared var with atomic %s  ...\n", "thread-1", sharedVar);
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        };
  • 运行结果
  • sharedVar = 20L显示的字节码如下:
代码语言:javascript
复制
 L3
 LINENUMBER 102 L3
 BIPUSH 20		// 压入线程的操作数栈
 INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$002 (I)I    //实例化并加载sharedVar并压入操作数栈,说明完成赋值操作
 POP   // 弹出数据
 L4
  • sharedVar ++ 显示的字节码如下:
代码语言:javascript
复制
 L3
 LINENUMBER 27 L3
 INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$000 ()I  // 实例化并加载sharedVar并压入操作数栈
 ICONST_1    // 将常量 1 压入线程操作数栈总
 IADD            // 执行sharedVar+1
 INVOKESTATIC com/xiaokunliu/blogs/thread/volatile2code/VolatileUsedClass.access$002 (I)I  // 重新实力化加载sharedVar,说明完成赋值操作
 POP
 L4
  • 运行分析:
    • 通过字节码可知,sharedVar ++;相比单纯赋值操作增加了一个添加的动作
    • 也就整体代码块存在在并发多线程下交替执行的两个操作,不具备原子性,volatile在这里是保证代码刷新到主内存,对于sharedVar = const 是具备原子性的

使用volatile小结

  • 对变量进行写操作的时候可以通过volatile来实现对其他线程的可见,同样在单步指令操作中是具备原子性,针对long或者double也起到具备原子性的作用
  • 对于需要用volatile修饰的变量来完成一系列的非单步操作运算是无法保证原子性,必须借助lock的方式来实现代码块的原子性
  • JDK关于原子性问题说明参考:
代码语言:javascript
复制
参考JDK关于原子性文档:
https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

感谢花时间阅读,如有用请转发或者点个好看,谢谢!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 疾风先生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. volatile的使用
  • 2. 原子性问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档