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变量的读写是原子性的。

posted @ 2017-11-20 11:48  Spground  阅读(283)  评论(0编辑  收藏  举报