牛客经典案例-Java中的原子操作-评论区里出大佬

以下多线程对int型变量x的操作,哪几个不需要进行同步(    )

A: x=y;
B: x++;
C: ++x;
D: x=1;

经典评论解析:

链接: https://www.nowcoder.com/questionTerminal/11a7d8d366d54ff9a07c1fa054d6541b
来源:牛客网
 

A.由于y的值不确定,所以要加锁;

B,C 两个在多线程情况下是必须要加锁的,因为他们是先被读入寄存器,然后再进行+1操作,如果没有加锁,那么可能会出现数据异常;

D 原子操作,所以不需要加锁

    原子性:指该操作不能再继续划分为更小的操作。

    Java中的原子操作包括:     

        1、除long和double之外的基本类型的赋值操作

        2、所有引用reference的赋值操作

        3、java.concurrent.Atomic.* 包中所有类的一切操作


链接:https://www.nowcoder.com/questionTerminal/11a7d8d366d54ff9a07c1fa054d6541b
来源:牛客网
 

A:在X86上它包含两个操作:读取y至寄存器,再把该值写入x。读y的值这个操作本身是原子的,把值写入x也是原子的,但是两者合起来不是原子操作,因为它不是不可再分的操作,所以需要进行同步

B、C:x++和++x这样的操作在多线程环境下是需要同步的。因为X86会按三条指令的形式来处理这种语句:从内存中读x的值到寄存器中,对寄存器加1,再把新值写回x 所处的内存地址

D:x=1是原子操作,不可再分因为x是int类型,32位CPU上int占32位,在X86上由硬件直接提供了原子性支持。实际上不管有多少个线程同时执行类似x=1这样的赋值语句,x的值最终还是被赋的值(而不会出现例如某个线程只更新了x的低16位然后被阻塞,另一个线程紧接着又更新了x的低24位然后又被阻塞,从而出现x的值被损坏了的情况)

百度解惑:

https://blog.csdn.net/zhaifengmin/article/details/46315003

Java中的原子操作包括:

1)除long和double之外的基本类型的赋值操作
2)所有引用reference的赋值操作
3)java.concurrent.Atomic.* 包中所有类的一切操作。

 但是java对long和double的赋值操作是非原子操作!!long和double占用的字节数都是8,也就是64bits。 在32位操作系统上对64位的数据的读写要分两步完成,每一步取32位数据。这样对double和long的赋值操作就会有问题:如果有两个线程同时写一个变量内存,一个进程写低32位,而另一个写高32位,这样将导致获取的64位数据是失效的数据。因此需要使用volatile关键字来防止此类现象。 volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。但是java的内存模型保证声明为volatile的long和double变量的get和set操作是原子的。(from http://www.iteye.com/topic/213794)

举个例子来说:(example is from http://stackoverflow.com/questions/17481153/long-and-double-assignments-are-not-atomic-how-does-it-matter)

public class UnatomicLong implements Runnable {
      private static long test = 0;

      private final long val;

      public UnatomicLong(long val) {
            this.val = val;
      }

      @Override
      public void run() {
            while (!Thread.interrupted()) {
                   test = val; //两个线程都试图将自己的私有变量val赋值给类私有静态变量test
            }
      }

      public static void main(String[] args) {
            Thread t1 = new Thread(new UnatomicLong(-1));
            Thread t2 = new Thread(new UnatomicLong(0));

            System.out.println(Long.toBinaryString(-1));
            System.out.println(pad(Long.toBinaryString(0), 64));

            t1.start();
            t2.start();

            long val;
           while ((val = test) == -1 || val == 0) { 
       //如果静态成员test的值是-1或0,说明两个线程操作没有交叉
        }

            System.out.println(pad(Long.toBinaryString(val), 64));
            System.out.println(val);

            t1.interrupt();
            t2.interrupt();
      }

      // prepend 0s to the string to make it the target length
      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;
      }
}

运行发现程序在while循环时进入了死循环,这是因为使用的JVM是64bits。 在64位JVM中double和long的赋值操作是原子操作。

在eclipse中修改jre为一个32bit的JVM地址,则会有如下运行结果:

1111111111111111111111111111111111111111111111111111111111111111

000000000000000000000000 000000000000000000000000 0000000000000000

000000000000000000000000 000000001111111111111111 1111111111111111 

//很明显test的值被破坏了

4294967295


https://blog.csdn.net/u014104269/article/details/82777928
 java内存模型要求lock、unlock、read、load、assign、use、store、write这8个操作都具有原子性,但是对于64位的数据类型(long和double),在模型中特别定义了一条先对宽松的规定:允许虚拟机将没有被volatile修饰的64位数据的读写操作划分为2次32位的操作来执行,即允许虚拟机实现选择可以不保证64位数据类型的read、store、load和write这4个操作的原子性。这就是所谓的long和double的非原子性协定(Nonatomic Treatment of double and long Variables)

                                                                                                                                             --《深入理解JVM虚拟机第二版》

首先:以上针对于32位虚拟机来讲的,64位JVM虚拟机不用考虑了,不存在这种情况;

其次:针对商业虚拟机来讲,基本都把long和double的操作实现为原子性操作;

以上,不必担心。(虽然虚拟机规定volatile修饰的long和double具有原子性),完全没有必要添加。
 

未完待续...............

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值