64位的long和double在Java中的读写操作

long和double类型

在java中,long和double类型的数据都占8个字节长度,即64位。对于long和double类型的读写操作,java并不能保证其原子性。下面的内容来自Java Language Specification:

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. a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half
  2. Writes and reads of volatile long and double values are always atomic.
  3. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
  4. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.

总结一下:

  1. 在java中,非volatile的double和long类型的写操作被分成了两个32位的写操作,所以对于64位的double和long类型的写操作就不是原子操作。
  2. 对于volatile类型的long和double类型的写操作是原子操作
  3. 对于引用类型的读写操作都是原子操作,无论引用类型的实际类型是32位的值还是64位的值。
  4. 在我们多线程情况下用到long和double的时候,尽量使用volatile或者同步对于数据操作进行控制。

int类型

int以及其他类型的读写操作都是原子操作。

有什么影响?

虽然java中int类型的读写操作是原子操作,而long类型的读写操作不是原子操作,但是在多线程环境下,为了避免数据不一致,我们都需要将int类型或者long类型使用volatile关键字或者synchronized机制进行处理,否则它们都不是线程安全的。那么问题来了,既然无论是否原子操作,为了保证线程安全都需要进行额外的volatile或者同步处理,那么对于int类型或者long类型来说,有什么区别呢? 比如下面的代码:

public final class SharedInt {

   private int value;

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

   public int getValue() {
      return this.value;
   }

}

public final class SharedLong {

    private long value;

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

    public long getValue() {
       return this.values;
    }
}

在多线程环境下对上面的SharedInt和SharedLong进行赋值和读取操作,都会出现线程安全问题,那么对于long和int来说,有啥不同呢?

答:

  1. 对于int类型的不恰当操作会造成的问题是:老的数据会被读取到。
  2. 对于long或double类型的不恰当操作会造成的问题是:可能从未出现过的数据被读取到。

举个例子: 假如两个线程A和B,A将int类型的值设置为1,而B将int类型的值设置为2,那么对于一个读取int类型值的线程来说,它读到的值要么是1,要么是2,不可能存在第三个数。但是对于long类型来说就不同了,因为long类型的写操作被分成了两个32位的写操作,所以假设A和B都要修改long类型的值,实际上一共是有四步操作,A的两个写操作和B的两个写操作,而这四个操作的不同顺序组合就会导致最终的结果既不是A想设置的结果也不是B想设置的结果。

总结

  1. java中对于long和double类型的写操作不是原子操作,而是分成了两个32位的写操作。读操作是否也分成了两个32位的读呢?在JSR-133之前的规范中,读也是分成了两个32位的读,但是从JSR-133规范开始,即JDK5开始,读操作也都具有原子性。
  2. java中对于其他类型的读写操作都是原子操作!除了long和double类型。Long和Double也是原子操作,因为对于它们的读写操作指的是其引用的读写操作。
  3. 对于long类型的不恰当操作可能读取到从未出现过的值。而对于int的不恰当操作则可能读取到旧的值。

参考资料

  1. http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
  2. http://stackoverflow.com/questions/17481153/long-and-double-assignments-are-not-atomic-how-does-it-matter
  3. 《Java高并发程序设计》(葛一鸣 郭超)1.5.1
  4. https://www.securecoding.cert.org/confluence/display/java/VNA05-J.+Ensure+atomicity+when+reading+and+writing+64-bit+values

转载于:https://my.oschina.net/newever/blog/738873

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值