JAVA 多线程基础:JAVA中double 和 long非原子读写问题

在解释这个问题之前,我们先来回顾下Java中基础数据类型所占的位数。

类型长度(位)字节
byte41
boolean41
int324
short162
long648
char162
float324
double648

可以看到对于double以及long两种基本数据类型,所占位数为64位。

而JVM却有32bit与64bit两种,也就是说在32bit JVM中不能将double 与 long类型的数据在一条指令中进行处理。为了处理64bit的数据,32bit的JVM会将一次对64bit的数据操作变为两次对32bit的操作。

具体来说,64bit的普通 long和double 数据在 32bit的JVM中 会被分为上32bit下32bit。

在一次读或写中,JVM将会分别读取 下32bit与上32bit。

代码演示


/**
 * double long 并发读写问题
 *
 * @author eventime
 */
public class DoubleLongTest {
    static long  number = 0L;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            int i = 0;
            while (i < 100000) {
                i ++;
                number = 4294967296L;
                long temp = number;
                if (temp != 4294967296L && temp != 1L) {
                    System.out.println(temp);
                    System.out.println("出现并发错误");
                    System.out.println("test1:" + i);
                    System.exit(0);
               }
            }
        });
        Thread t2 = new Thread(() -> {
            int i = 0;
            while (i < 100000) {
                i ++;
                number = 1L;
                long temp = number;
                if (temp != 4294967296L && temp != 1L) {
                    System.out.println(temp);
                    System.out.println("出现并发错误");
                    System.out.println("test2:" + i);
                    System.exit(0);
                }
            }
        });

        String arch = System.getProperty("sun.arch.data.model");
        System.out.println(arch+"-bit");
        t2.start();
        t1.start();
        t1.join();
        t2.join();
    }
}

上文的代码中,我定义了t1,t2两个线程。两线程都是更改一个不加锁的静态变量number并且判断输出。

运行程序我们可能得到下面的结果:

32-bit

出现并发错误

错误值为:0

32-bit

出现并发错误

错误值为:4294967297

32-bit

如果还记并发编程中的竞态条件问题,按理来说上面程序在启动后 number应该为4294967296L或者1L。但是为什么会出现0L和:4294967297L呢?

让我们观察两个线程的具体执行过程:

t1负责每次循环将静态变量number赋值为 4294967296L, 这个数字的二进制表示为

000000000000000000000000000000001 0000000000000000000000000000000

可以看到他的下32位全部为0;

t2负责每次循环将静态变量number赋值为 1L, 这个数字的二进制表示为

000000000000000000000000000000000 0000000000000000000000000000001

可以看到他的上32位全部为0;

在线程的执行过程中,由于long这样的64bit数据读写并不是原子的,所以对其数据的操作将会分为上32bit与下32bit。

正常情况如图所示,操作时序图期望应该是这样,读出的数据要么是1L要么是4294967296L。

画板

但是由于写入和读取并非原子操作,所以可能出现以下的时序错误:

得到4294967297L

画板

得到0L

画板

解决方案

解决double long非原子读写问题的方案很简单。

  1. 使用64bit的JVM,在64bit中,对于64bit的数据读写默认为原子类型。
  2. 在32bit的JVM中,对long double这样的数据添加 volatile 关键字。添加后JVM便会将double long的读写变为原子读写。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值