• Java的long、double类型的原子性读取问题


    Java的long、double类型的原子性读取问题

    In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn’t happen at all. No side effects of an atomic action are visible until the action is complete.

    以上是关于原子性的操作的相关描述。

    在Java中,以下的操作可以认为是原子操作

    1. 对于引用变量、大多数的原始类型变量的读、写(所有的类型除了longdouble)都是原子性的(这个只有才32位的JVM成立)
    2. 所有申明为volatile的变量(包括longdouble变量)的读、写都是原子性的

    在Java中,自增、自减操作都不是原子性操作,很容易理解。但是longduoble的读写却不是原子性的问题,却不太好理解。

    检验

    在32位JVM上,通过两个线程对同一个成员变量进行读写,来测试longdouble类型变量的读写不是原子性操作。

    
    public class UnatomicLongDemo implements Runnable {
    
        private static long test = 0;
    
        private final long val;
    
        public UnatomicLongDemo(long val) {
            this.val = val;
        }
    
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                test = val;//两个线程同时断写test变量,如果test变量的读写操作是原子性的,那么test只能是-1或者0
            }
        }
    
        public static void main(String[] args) {
            Thread t1 = new Thread(new UnatomicLongDemo(-1));
            Thread t2 = new Thread(new UnatomicLongDemo(0));
    
            System.out.println(Long.toBinaryString(-1));
            System.out.println(pad(Long.toBinaryString(0), 64));
    
            t1.start();
            t2.start();
    
            long switchVal;
            while ((switchVal = test) == -1 || switchVal == 0) {
                //如果test、switchVal的操作是原子性的,那么就应该是死循环,否则就会跳出该循环
                System.out.println("testing...");
            }
    
            System.out.println(pad(Long.toBinaryString(switchVal), 64));
            System.out.println(switchVal);
    
            t1.interrupt();
            t2.interrupt();
        }
    
        //将0补齐到固定长度
       private static String pad(String s, int targetLength) {
            int n = targetLength - s.length();
            for (int x = 0; x < n; x++) {
                s = "0" + s;
            }
            return s;
        }
    
    }
    

    测试结果为

    这里写图片描述

    通过这个例子可以看出

    1. 在32位JVM上,对long型变量test的读取或者对long型变量switchVal的写入不是原子性的。

    2. 非原子性的读、写只是造成longdouble类型变量的高、低32位数据不一致

    这是由于在32位JVM中对64位的数据的读、写分两步,每一步读或者写32位的数据,这样就会造成两个线程对同一个变量的读写出现一个线程写高32位、另一个线程写入低32位数据。这样此变量的数据就出现不一致的情况。这时候volatile关键字可以防止这种现象发生,因为java的内存模型保证了valotile修饰的longdouble变量的读写是原子性的。

  • 相关阅读:
    JavaScript DOMContentLoaded 和 load事件的区别
    Redux源码分析之combineReducers
    Redux源码分析之createStore
    LeetCode003 无重复字符的最长子串
    Navicat Premium 15 永久破解和2021版本最新IDEA破解(亲测有效)
    在Win10中安装虚拟机:VMware Workstation Player+Ubuntu20.04
    C# 对象复制三种方法效率对比——反射、序列化、表达式树
    nginx在个人网站上的优化(一)
    VUE3.0的打包配置修改
    Jenkins构建项目连接Repository URL的填坑之路
  • 原文地址:https://www.cnblogs.com/Spground/p/8536143.html
Copyright © 2020-2023  润新知