研究原子类型,我们先从最常用的AtomicInteger开始看起。
我们可以先看看AtomicInteger的使用方法:
1 | AtomicInteger i = new AtomicInteger(0); |
用法还是很简单的,我们也可以看到AtomicInteger等一系列原子数据类是为了解决多线程访问Integer变量导致可能不出现的结果所设计实现的一个基于原子操作的Integer类。
那么老规矩,研究源码, 基于jdk 1.8.0_05 。
看看构造方法。
1 | private volatile int value; |
可以看到实际上AtomicInteger用一个volatile int保存了当前要进行一系列操作的int对象。当然也有一个无参数的构造方法,可以通过set()方法给value赋值,实现的功能是一样的。
对于这个value的操作就涉及到了一个Unsafe类。
1 | private static final Unsafe unsafe = Unsafe.getUnsafe(); |
Unsafe类主要是用来实现一个不安全的操作的比较特殊的类,Unsafe能够提供很多保证安全的方法,在这里主要是提供了CAS操作。
CAS对int类型的数据进行操作时,主要使用了这个方法:
1 | public final native boolean compareAndSwapInt(Object object, long valueOffset, int expect, int update); |
而这里的valueOffset是一个全局的final long 数据,表示一个内存位置。
1 | private static final long valueOffset; |
这里的value就是之前提到的volatile int 的值value,这里主要是为了保证这个value的可见性,某个线程修改了value后,其他线程也能读取到正确的值。
另外,通过unsafe提供的CAS方法,可以在AtomicInteger中修改值。1
2
3public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
所以我们可以看到通过CAS来保证原子性,通过volatile保证了可见性。
阅读源码发现,AtomicInteger还有几个很有意思的方法。也是我们一开始就见到的常用用法。
1 | public final int getAndAdd(int delta) { |
用法我在一开始也通过例子解释了,getAndAdd是先通过unsafe.getAndAddInt获取当前的安全值然后再原子操作加上传进来的参数delta,而addAndGet是同样的unsafe.getAndAddInt获取当前的
安全值,并且原子操作加上delta。这里使用的同样的原子操作,但是有一个trick是同样的原子操作进行先取值再加值,addAndGet是返回了取出的值直接加上delta。这里真的很巧妙啊。为什么我要说这里很有意思呢? 我们可以看看在jdk 1.7的实现。
1 | public final int getAndAdd(int delta) { |
可以看到,1.8到1.7,将AtomicInteger中暴露的Unsafe的方法使用的更少了。代码看着更简洁了。
并且for循环也换成了do{}while(), 虽然性能没什么改变,但是更直观了。
好,回到原子性上来。既然提到了CAS,我们也看到了Unsafe中的CAS的方法使用,主要在native层使用原子指令实现对值的修改,需四个参数,其中valueOffSet是内存地址,expect是旧的期望正确的值,update是新的值。
只有旧值匹配时,才会将旧值更新为新值。关于CAS的具体学习,我们以后再继续深入,今天的主角是AtomicInteger。
除了getAndAdd,还有getAndIncrement等方法,其实本质都是一样的,只不过delta确认为1而已。
另外,相比较jdk 1.7, 在jdk1.8中新拓展了两个方法:
1 | public final int getAndUpdate(IntUnaryOperator updateFunction) { |
简单用法如下:
1 | AtomicInteger ai = new AtomicInteger(10); |
IntUnaryOperator是一个jdk 1.8的新增类,主要完成对单一值运算操作得到一个新结果的过程。例如上面的例子中就是将偶数减2,奇数减1的一个“side-effect-free” 函数,也就是对单一值进行运算。
除了以上对各种值的原子性修改以外,AtomicInteger还提供了一些方法将int转为其他类型。
1 | public long longValue() { |
好了,关于AtomicInteger的学习研究先到这儿吧,主要需要弄懂Unsafe在其中起的作用以及jdk 1.8的一些新变化。其实其他几个原子基本数据类的方法和实现与AtomicInteger差不多,就不一一介绍了,搞懂这一个,其他就触类旁通了。