java内存模型及常见的几种非原子性操作

java内存模型(JMM)

java内存模型 (java memory model JMM)指定了java虚拟机如何与计算机的主存RAM进行工作(可以类比RAM和cpu L1 L2 L3高速缓存)
在这里插入图片描述

java内存模型定义了线程和主内存之间的抽象关系

线程不能直接操作主内存,只有先操作了工作内存之后才能写入主内存。

工作内存和java内存模型一样也是一个抽象的概念,它其实并不存在,其涵盖了缓存、寄存器、编译器优化以及硬件等。

jvm采用java内存模型机制来屏蔽各个平台和操作系统之间内存访问的差异,以实现让java程序在各种平台下达到一致的内存访问效果。比如C语言中的int变量,在某些平台下占用两个字节的内存,在某些平台下占用四个字节的内存,java则在任何平台下,int类型就是四个字节,这就是所谓的一致内存访问效果。

1. 自增操作

++这个操作,分成三步:

  1. 线程从主内存中读出来最新的value值
  2. 会放到线程的虚拟机栈的栈帧中的操作栈上,压进去,然后弹出,供cpu计算加1,计算完成再压入,再弹出
  3. 线程将新的值再写回到内存中。

可能存在的问题:

  1. 在线程执行到第2步的时候,另一个线程在可能已经修改了主内存中value的值,而写写回后将另一个线程的操作覆盖掉了
  2. 要考虑并发和并行的情况
    • 当线程1和线程2在同一个cpu上进行切换,会共享工作内存,这是并发的情况
    • 当线程1和线程2在不同cpu上进行切换,这是并行的情况

2. instance = new Singleton();

instance = new Singleton();

编译为JVM代码,需要做三件事情

  1. 要分配内存

  2. 调用Singleton的构造函数,字节码指令是invokeSpecial

  3. 给instance赋值,指向新创建的对象

其中2和3是可能发生指令重排操作的,这也是单例模式下双重校验仍要使用volatile的原因

3. y = x;

y=x是非原子性的,因为其包含两个步骤:

  1. 执行线程从主内存中读取x的值(如果x已经存在于执行线程的工作内存中,则直接获取),然后将其存入当前线程的工作内存中

  2. 在执行线程的工作内存中修改y的值为x,然后将y的值写入主内存中

这两步都是原子操作,但合在一起就不是原子操作了

4. 64位数据操作

比如在AtomicLong中,

/**
 * Records whether the underlying JVM supports lockless
 * compareAndSet for longs. While the intrinsic compareAndSetLong
 * method works in either case, some constructions should be
 * handled at Java level to avoid locking user-visible locks.
 */
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

这个是AtomicInteger没有的

long是64位的,

与cpu通信是通过总线进行的,有数据总线、控制总线、地址总线。

如果数据总线是32位的,那么long就要分两次来传输给cpu,这样可能就不好保证原子性了

如果不支持,可能就需要对总线进行加锁了,比如对数据总线进行加锁,我要把高位和低位都拿过去,然后再把锁释放,这样可能效率要低一些。如果支持,就不需要锁总线

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值