Java并发编程-原子类实现

前言

为了研究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;

该类共有三个成员属性。this

  • 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):code

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的实现依赖于两个条件:rem

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

了解常见操做getAndIncrement():get

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操做,若是再执行该操做过程当中,内存数据发生变动,则执行失败,但循环操做直至成功。
相关文章
相关标签/搜索