AtomicXXX系列类使用分析
程序员文章站
2022-06-15 12:39:58
本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。 "并发编程系列博客传送门" 在 中,普通的原子类型有以下四种: 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; }
策略更新
下面几个方法个人觉得不是很有用,和上面的区别就是更新的值不是穿进去的,而是通过intunaryoperator
和intbinaryoperator
接口算出来的。
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机制来实现共享变量操作的线程安全的。感兴趣的同学可以自己分析下,也是比较简单的。
推荐阅读
-
Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
-
Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
-
php抽象类使用要点与注意事项分析
-
一个简单Ajax类库及使用方法实例分析
-
Java日期时间API系列30-----Jdk8中java.time包中的新的日期时间API类,减少时间精度方法性能比较和使用。
-
增长黑客系列:今天比昨天增长多少?快使用环比函数来分析日志
-
Mac下使用IntelliJ IDEA导入Spring源码-Spring源码分析系列(一)
-
php的db类库Eloquent单独使用系列(11)- 添加模型属性
-
php的db类库Eloquent单独使用系列(9)- 多对多关联 - 表关联自身
-
JVM系列(1)深入浅出分析JVM类加载机制