欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AtomicXXX系列类使用分析

程序员文章站 2022-03-10 12:32:00
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。 "并发编程系列博客传送门" 在 中,普通的原子类型有以下四种: AtomicBoolean:提供对基本数据类型boolean的原子性更新操作。 AtomicInteger:提供对基 ......

本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。


java.util.concurrent.atomic中,普通的原子类型有以下四种:

  • atomicboolean:提供对基本数据类型boolean的原子性更新操作。
  • atomicinteger:提供对基本数据类型int的原子性更新操作。
  • atomiclong:提供对基本数据类型long的原子性更新操作。
  • atomicreference:这是一个泛型类,提供对引用类型的原子性更新操作。

数组相关的操作类有:

  • atomiclongarray:提供对int[]数组元素的原子性更新操作。
  • atomicintegerarray:提供对long[]数组元素的原子性更新操作。
  • atomicreferencearray:提供对引用类型[]数组元素的原子性更新操作。

由于上面的原子操作类的实现原理差不多,我们这边就选择atomicinteger来分析。

代码分析

构造函数

public class atomicinteger extends number implements java.io.serializable {
    private static final long serialversionuid = 6214790243416807050l;

    //unsafe类提供底层的cas机制 
    private static final unsafe unsafe = unsafe.getunsafe();
    //valueoffset是value值的内存地址值偏移值,这个值的作用是获取value在主内存中的值
    private static final long valueoffset;
    //类加载的时候获取valueoffset的值
    static {
        try {
            valueoffset = unsafe.objectfieldoffset
                (atomicinteger.class.getdeclaredfield("value"));
        } catch (exception ex) { throw new error(ex); }
    }

    //atomicinteger具体的值存放在这个变量中,
    //这个变量使用volatile修饰,具有可见性
    private volatile int value;
    
    public atomicinteger(int initialvalue) {
        value = initialvalue;
    }

    //默认为0
    public atomicinteger() {
    }
}

get和set方法分析

//value使用volatile修饰,每次能拿到最新值
public final int get() {
    return value;
}

//value使用volatile修饰,赋值操作具有原子性,所以这个操作也是线程安全的
//这个方法和compareandset方法的区别是:compareandset方法会判断预期值和当前值,而set方法不会做任何判断,直接更新
// set方法不会在意原始值是多少,而compareandset会确保主内存中的值和预期值相等才更新。
public final void set(int newvalue) {
    value = newvalue;
}

//这个方法可能比较令人疑惑,我查了下unsafe的putorderedint方法,如下

/**  sets the value of the integer field at the specified offset in the
  * supplied object to the given value. this is an ordered or lazy
  * version of <code>putintvolatile(object,long,int)</code>, which
  * doesn't guarantee the immediate visibility of the change to other
  * threads. it is only really useful where the integer field is
  * <code>volatile</code>, and is thus expected to change unexpectedly.
  */
上面的意思大致是:putorderedint方法不保证可见性,只有在变量是volatile修饰时才有用,
我们这边的value变量就是用volatile修饰的,所以我认为atomicinteger的`set`方法和`lazyset`方法
功能是一致的。
public final void lazyset(int newvalue) {
    unsafe.putorderedint(this, valueoffset, newvalue);
}

//将value设置成给定值,并返回旧值
public final int getandset(int newvalue) {
    return unsafe.getandsetint(this, valueoffset, newvalue);
}
//使用cas机制更新
public final boolean compareandset(int expect, int update) {
    return unsafe.compareandswapint(this, valueoffset, expect, update);
}
//使用cas机制更新
public final boolean weakcompareandset(int expect, int update) {
    return unsafe.compareandswapint(this, valueoffset, expect, update);
}
//cas加1,并且返回原始值
public final int getandincrement() {
    return unsafe.getandaddint(this, valueoffset, 1);
}
//cas减1,并且返回原始值
public final int getanddecrement() {
    return unsafe.getandaddint(this, valueoffset, -1);
}
//cas加减delta值,并且返回原始值
public final int getandadd(int delta) {
    return unsafe.getandaddint(this, valueoffset, delta);
}
//cas加1,并且返回最新值
public final int incrementandget() {
    return unsafe.getandaddint(this, valueoffset, 1) + 1;
}
//cas减1,并且返回最新值
public final int decrementandget() {
    return unsafe.getandaddint(this, valueoffset, -1) - 1;
}
//cas加减delta值,并且返回最新值
public final int addandget(int delta) {
    return unsafe.getandaddint(this, valueoffset, delta) + delta;
}

策略更新

下面几个方法个人觉得不是很有用,和上面的区别就是更新的值不是穿进去的,而是通过intunaryoperatorintbinaryoperator接口算出来的。

public final int getandupdate(intunaryoperator updatefunction) {
    int prev, next;
    do {
        prev = get();
        next = updatefunction.applyasint(prev);
    } while (!compareandset(prev, next));
    return prev;
}

public final int updateandget(intunaryoperator updatefunction) {
    int prev, next;
    do {
        prev = get();
        next = updatefunction.applyasint(prev);
    } while (!compareandset(prev, next));
    return next;
}

public final int getandaccumulate(int x, intbinaryoperator accumulatorfunction) {
    int prev, next;
    do {
        prev = get();
        next = accumulatorfunction.applyasint(prev, x);
    } while (!compareandset(prev, next));
    return prev;
}

 public final int accumulateandget(int x,intbinaryoperator accumulatorfunction) {
    int prev, next;
    do {
        prev = get();
        next = accumulatorfunction.applyasint(prev, x);
    } while (!compareandset(prev, next));
    return next;
}

简单总结

总体来说,atomicboolean、atomicinteger、atomiclong和atomicreference原理比较简单:使用cas保证原子性,使用volatile保证可见性,最终能保证共享变量操作的线程安全。

atomiclongarray、atomicintarray和atomicreferencearray的实现原理略有不同,是用cas机制配合final机制来实现共享变量操作的线程安全的。感兴趣的同学可以自己分析下,也是比较简单的。