首页 > 编程语言 >详细谈谈Java中long和double的原子性

详细谈谈Java中long和double的原子性

时间:2023-02-06 13:45:12浏览次数:47  
标签:Java double long 原子 64 JVM 操作

目录
  • 前言
  • JVM中对long的操作是不是原子操作?
  • 为什么对long的操作不是原子的?
  • 在硬件,操作系统,JVM都是64位的情况下呢?
  • 总结

 

前言

java中基本类型中,long和double的长度都是8个字节,32位(4字节)处理器对其读写操作无法一次完成,那么,JVM,long和double是原子性的吗?

 

JVM中对long的操作是不是原子操作?

首先,通过一段程序对long的原子性进行判断。测试程序如下:


public class LongAtomTest implements Runnable {

    private static long field = 0;

    private volatile long value;

    public long getValue() {
        return value;
    }

    public void setValue(long value) {
        this.value = value;
    }

    public LongAtomTest(long value) {
        this.setValue(value);
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 100000) {
            LongAtomTest.field = this.getValue();
            i++;
            long temp = LongAtomTest.field;
            if (temp != 1L && temp != -1L) {
                System.out.println("出现错误结果" + temp);
                System.exit(0);
            }
        }
        System.out.println("运行正确");
    }

    public static void main(String[] args) throws InterruptedException {
        // 获取并打印当前JVM是32位还是64位的
        String arch = System.getProperty("sun.arch.data.model");
        System.out.println(arch+"-bit");
        LongAtomTest t1 = new LongAtomTest(1);
        LongAtomTest t2 = new LongAtomTest(-1);
        Thread T1 = new Thread(t1);
        Thread T2 = new Thread(t2);
        T1.start();
        T2.start();
        T1.join();
        T2.join();
    }

}

可以看到,程序中有两条线程t1,t2; t1,t2各自不停的给long类型的静态变量field赋值为1,-1; t1,t2每次赋值后,会读取field的值,若field值既不是1又不是-1,就将field的值打印出来

如果对long的写入和读取操作是原子性的,那么,field的值只可能是1或者-1

运行结果如下

32-bit 出现错误结果-4294967295 运行正确

可以看出,当线程t1,t2同时对long进行写的时候,long出现了既不是t1写入的值,又不是t2写入的值。可以推测,jvm中对long的操作并非原子操作。

 

为什么对long的操作不是原子的?

JVM内存模型中定义了8中原子操作:

  1. lock:将一个变量标识为被一个线程独占状态
  2. unclock:将一个变量从独占状态释放出来,释放后的变量才可以被其他线程
  3. read:将一个变量的值从主内存传输到工作内存中,以便随后的load操作
  4. load:把read操作从主内存中得到的变量值放入工作内存的变量的副本中
  5. use:把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令
  6. assign:把一个从执行引擎接收到的值赋给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时,都要使用该操作
  7. store:把工作内存中的一个变量的值传递给主内存,以便随后的write操作
  8. write:把store操作从工作内存中得到的变量的值写到主内存中的变量

其中,与赋值,取值相关的包括 read,load,use,assign,store,write

按照这个规定,long的读写都是原子操作,与我们的实践结果相反,为什会导致这种问题呢?

对于32位操作系统来说,单次次操作能处理的最长长度为32bit,而long类型8字节64bit,所以对long的读写都要两条指令才能完成(即每次读写64bit中的32bit)。如果JVM要保证long和double读写的原子性,势必要做额外的处理。 那么,JVM有对这一情况进行额外处理吗? 针对这一问题可以参考Java语言规范文档:jls-17 Non-Atomic Treatment of double and long

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.

Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perfORM writes to long and double values atomically or in two parts.

Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.

从规定中我们可以知道

  1. 对于64位的long和double,如果没有被volatile修饰,那么对其操作可以不是原子的。在操作的时候,可以分成两步,每次对32位操作。
  2. 如果使用volatile修饰long和double,那么其读写都是原子操作
  3. 对于64位的引用地址的读写,都是原子操作
  4. 在实现JVM时,可以自由选择是否把读写long和double作为原子操作
  5. 推荐JVM实现为原子操作

从程序得到的结果来看,32位的HotSpot没有把long和double的读写实现为原子操作。 在读写的时候,分成两次操作,每次读写32位。因为采用了这种策略,所以64位的long和double的读与写都不是原子操作。

 

在硬件,操作系统,JVM都是64位的情况下呢?

对于64bit的环境来说,单次操作可以操作64bit的数据,即可以以一次性读写long或double的整个64bit。因此我们可以猜测,在64位的环境下,long和double的读写有可能是原子操作。 在换了64位的JVM之后,多次运行,结果都是正确的

64-bit 运行正确 运行正确

结果表明,在64bit的虚拟机下,long的处理是原子性的。

标签:Java,double,long,原子,64,JVM,操作
From: https://www.cnblogs.com/dituirenwu/p/17095181.html

相关文章

  • javascript之预编译
        Javascript按照<script>段的方式进行预编译处理相关的代码段,并且按照先预定义变量,再预定义函数的方式进行预编译!而且无论变量/函数在段中的任何地点进行显式......
  • 验证邮件地址的Java正则表达式
    最近写了个Java正则表达式来验证RFC5322规范的邮件地址,这个邮件地址比较复杂,对于这样一个地址:userName@domainName,它满足以下条件:对于userName1、允许使用以下所有字符作为......
  • Java反编译工具JD-GUI以及Eclipse的反编译插件
    什么是反编译高级语言源程序经过编译变成可执行文件,反编译就是逆过程。但是通常不能把可执行文件变成高级语言源代码,只能转换成汇编程序。反编译是一个复杂的过程,所以越是......
  • 由Java正则表达式的灾难性回溯引发的高CPU异常:java.util.regex.Pattern$Loop.match
    问题与分析某天领导report了一个问题:线上的CPU自从上一个版本迭代后就一直处于居高不下的状况,领导看着这段时间的曲线图判断是有两条线程在不停的死循环。接到任务后去查看......
  • Java下一代高并发技术——虚拟线程“JEP 425: Virtual Threads (Preview)”
    很多语言都有类似于“虚拟线程”的技术,比如Go、C#、Erlang、Lua等,他们称之为“协程”。不管是虚拟线程还是协程,他们都是轻量级线程,其目的都是为了提高并发能力。本节详细......
  • 【Java 数据结构及算法实战】系列 016:HJ2 计算某字符出现次数
    描述写出一个程序,接受一个由字母、数字和空格组成的字符串,和一个字母,然后输出输入字符串中该字母的出现次数。不区分大小写,字符串长度小于500。输入描述:第一行输入一个由字......
  • 【Java数据结构及算法实战】系列007:Java队列01——Queue概述
    队列与栈类似,也是一种运算受限的线性表。队列则被限定在表尾进行插入、在表头进行删除,这种数据结构,实现了FIFO(FirstInFirstOut,先进先出)或者是LILO(LastInLastOut,后进后......
  • java注解与反射详解
    一、注解篇1.1、注解的基本概念注解:一种代码级别的说明,它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次;它可以声明在包、类、字段、方法、局部变量......
  • javascript提交示例
    <td>@Html.ActionLink("编辑","Edit",new{id=1})<text>|</text>@Html.ActionLink("删除","Delete",new{id=2})<text>|</text><ahref="javascrip......
  • java中的繁体字/简体字转换
    一、引入pom文件<!--https://mvnrepository.com/artifact/com.spreada/ZHConverter--><dependency><groupId>com.spreada</groupId><artifactId>ZHConverte......