Java并发编程-原子类实现

智码棱镜客
• 阅读 2549

前言

为了研究Java对原子类的实现,从AtomicInteger类开始,分析Java如果对原子操作的实现。

什么是原子操作?

原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何上下文的切换。
注:原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割只执行其中的一部分。

源码分析:

首先从AtomicInteger类的属性聊起:

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
private volatile int value;

该类共有三个成员属性。

  • unsafe:该类是JDK提供的可以对内存直接操作的工具类。
  • valueOffset:该值保存着AtomicInteger基础数据的内存地址,方便unsafe直接对内存的操作。
  • value:保存着AtomicInteger基础数据,使用volatile修饰,可以保证该值对内存可见,也是原子类实现的理论保障。

再谈静态代码块(初始化)

    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

该过程实际上就是计算成员变量value的内存偏移地址,计算后,可以更直接的对内存进行操作。
了解核心方法compareAndSet(int expect,int update):

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

在该方法中调用了unsafe提供的服务:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

下面看看这个类在JDK中是如何实现的:

jboolean sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset,jint expect, jint update)  {  
  jint *addr = (jint *)((char *)obj + offset); //1
  return compareAndSwap (addr, expect, update);
}  

static inline bool compareAndSwap (volatile jlong *addr, jlong old, jlong new_val)    {    
  jboolean result = false;    
  spinlock lock;    //2
  if ((result = (*addr == old)))    //3
    *addr = new_val;    //4
  return result;  //5
}  
  1. 通过对象地址和value的偏移量地址,来计算value的内存地址。
  2. 使用自旋锁来处理并发问题。
  3. 比较内存中的值与调用方法时调用方所期待的值。
  4. 如果3中的比较符合预期,则重置内存中的值。
  5. 如果成功置换则返回true,否则返回false;

综上所述:compareAndSet的实现依赖于两个条件:

  • volatile原语:保证在操作内存的值时,该值的状态为最新的。(被volatile所修饰的变量在读取值时都会从变量的地址中读取,而不是从寄存器中读取,保证数据对所有线程都是可见的)
  • Unsafe类:通过该类提供的功能,可以直接对内存进行操作。

了解常见操作getAndIncrement():

    return unsafe.getAndAddInt(this, valueOffset, 1);
}

同样使用unsafe提供的方法:

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);//1
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//2
    return var5;
}
 
//getIntVolatile方法native实现
jint sun::misc::Unsafe::getIntVolatile (jobject obj, jlong offset)    
{    
  volatile jint *addr = (jint *) ((char *) obj + offset);    //3
  jint result = *addr;    //4
  read_barrier ();    //5
  return result;    //6
}  
inline static void read_barrier(){
  __asm__ __volatile__("" : : : "memory");
}
  1. 通过volatile方法获取当前内存中该对象的value值。
  2. 计算value的内存地址。
  3. 将值赋值给中间变量result。
  4. 插入读屏障,保证该屏障之前的读操作后后续的操作可见。
  5. 返回当前内存值
  6. 通过compareAndSwapInt操作对value进行+1操作,如果再执行该操作过程中,内存数据发生变更,则执行失败,但循环操作直至成功。
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java操作lua脚本
java操作lua脚本实例1.前言在上一篇文章Redis中使用Lua脚本来实现并发下的原子操作中我对Lua语言的一些简单的语法及其在Redis中的操作进行了介绍,但是在Java开发中我们还需要进一步的学习才能使这种技术落地。今天就结合SpringDataRedis这个我们经常使用的Redis开发组件来实际尝试一下Lua脚本。
Wesley13 Wesley13
3年前
java面试题汇总,不断更新中。。。
JVM,并发,锁相关:1.请你谈谈对volatile的理解,volatile是否存在伪共享问题。2.cas你知道吗?3.原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?4.公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解?请手写一个自旋锁。5.CountDownLatch、CyclicBarrier、S
Wesley13 Wesley13
3年前
C++原子类实现
引言在系统实现的过程中,经常需要用到计数功能,为了多线程下的安全使用,我自己定义了一个原子类。基于Mutex的实现我基于Mutex实现了一个简单的原子类,代码如下/说明:自定义整数操作的原子类,减少代码中的各种锁/ifndef_ATOMIC_INT64
Wesley13 Wesley13
3年前
Java 深入理解volatile关键字
我们知道Java中volatile实现了修饰变量的原子性以及可见性,并且为了实现多线程环境下的线程安全,禁止了指令重排。首先我们先来了解一下happensbefore原则、asifserial语义以及数据依赖性,引用自《Java并发编程的艺术》happensbefore简介从JDK5开始,Java使用新的JSR133内存模型
Wesley13 Wesley13
3年前
Java 之 synchronized 详解
一、概念synchronized是Java中的关键字,是利用锁的机制来实现同步的。锁机制有如下两种特性:互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。可见性:必须确
Wesley13 Wesley13
3年前
Java原子类操作原理剖析
◆CAS的概念◆对于并发控制来说,使用锁是一种悲观的策略。它总是假设每次请求都会产生冲突,如果多个线程请求同一个资源,则使用锁宁可牺牲性能也要保证线程安全。而无锁则是比较乐观的看待这个问题,它会假设每次访问都没有冲突,这样就提高了效率。但是事实难料、这个冲突是避免不了的,无锁也考虑到了肯定会遇到冲突,对于冲突的解决无锁就使用一种比较交换(CA
Wesley13 Wesley13
3年前
MySQL数据库InnoDB存储引擎Log漫游(1)
作者:宋利兵来源:MySQL代码研究(mysqlcode)0、导读本文介绍了InnoDB引擎如何利用UndoLog和RedoLog来保证事务的原子性、持久性原理,以及InnoDB引擎实现UndoLog和RedoLog的基本思路。00–UndoLogUndoLog是为了实现事务的原子性,
Wesley13 Wesley13
3年前
Java中的13个原子操作类
点击上方“Java中文社群”,选择“设为星标(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzUzMTA2NTU2Ng%3D%3D%26mid%3D2247486188%26idx%3D3%26sn%3Df160d91ea2
Wesley13 Wesley13
3年前
JDK中的Atomic包中的类及使用
引言Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。Atomic包介绍在JDK1
Wesley13 Wesley13
3年前
Java 开发, volatile 你必须了解一下
并发的三个特性首先说我们如果要使用volatile了,那肯定是在多线程并发的环境下。我们常说的并发场景下有三个重要特性:原子性、可见性、有序性。只有在满足了这三个特性,才能保证并发程序正确执行,否则就会出现各种各样的问题。原子性,上篇文章说到的CAS和Atomic\类,可以保证简单操作的原子性,对
智码棱镜客
智码棱镜客
Lv1
稚子牵衣问归来何太迟?共谁争岁月;赢得鬓边丝?
文章
5
粉丝
0
获赞
0