因为java在concurrent包中大量的使用到了AtomicInteger,于是打算从AtomicInteger开始对concurrent包的各个类做一番探究。
如果想对int型数据进行原子操作,那么推荐使用AtomicInteger。
当然也可以使用synchronized代码块,但使用AtomicInteger更高效。
如下代码说明了使用AtomicInteger和使用Integer的区别:
static AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) {
Runnable[] threads = new Runnable[300];
for(int j=0;j<300;j++){
threads[j] = new Runnable() {
public void run() {
System.out.println(i.incrementAndGet());
}
};
}
for(int j=0;j<300;j++){
new Thread(threads[j]).start();
}
}
上述的代码最后输出结果中最大为300.
再看下面的代码:
static Integer i = new Integer(0);
public static void main(String[] args) {
Runnable[] threads = new Runnable[300];
for(int j=0;j<300;j++){
threads[j] = new Runnable() {
public void run() {
System.out.println(++i);
}
};
}
for(int j=0;j<300;j++){
new Thread(threads[j]).start();
}
}
毫无疑问,将AtomicInteger换成Integer后,多线程没有进行同步,因此一般来说最后的结果中不会有300.
查看AtomicInteger的源码发现,其实同步多线程的操作是通过Unsafe对象来实现的。
于是来先来看看Unsafe吧。
Unsafe
Unsafe类提供了硬件级别的原子操作
对于不想细究的人来说,只需要记住一上一点就行了。即Unsafe提供了和操作系统、内存层面的操作,Unsafe类里面包含大量的native方法,其实就是将这些方法封装为Unsafe类,表示这些方法是底层实现的。
CAS
说道Unsafe,最著名的就是CAS操作,即Compare And Swap,比较并交换。
希望大家不要被这个词误导,CAS真的就是这么简单的意思,硬要在深挖一层的话那就是其实现的细节了。CAS使用Unsafe实现,具体来说是调用native方法,最常用的CAS方法如下:
public final class Unsafe {
//...
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
var1是比较值所属的对象,var2需要比较的值(但实际是使用地址偏移量来实现的),如果var1对象中偏移量为var2处的值等于var4,那么将该处的值设置为var5并返回true,如果不等于var4则返回false。
简单来说这就是CAS的全部了,作为使用者,记住这些就OK.
park
还包括unpark方法。
Unsafe提供了park方法和unpark方法用于挂起线程和恢复线程
park系列方法关系到锁的实现,具体到锁在讨论。
定位于修改
Unsafe还可以用于定位对象的属性的内存位置,修改属性值。
- 定位属性位置
对于一个类来说,该类的一个对象,其某个属性值相对于该对象的内存空间的位置是固定的,而类Unsafe可以帮我们获取到这个偏移量。 - 定位属性值
有了属性的偏移量,Unsafe还提供了根据对象,偏移量来获取该对象的值的方法。
上述两个功能的方法参考如下:
public final class Unsafe {
//获取类中属性的偏移量
public native long objectFieldOffset(Field var1);
//获取对象var1内部偏移量为var2的类型为Int的值
public native int getIntVolatile(Object var1, long var2);
//修改对象var1内部偏移量为var2的类型为Int的值为var4
public native void putIntVolatile(Object var1, long var2, int var4);
分配与释放内存
Unsafe甚至提供手动操作内存的操作,正如类名一样,这些操作都是不安全的,我们不应该去使用。
贴一份参考资料,讲unsafe的Java中Unsafe类详解
到这里我么简单浏览了一下Unsafe类,也知道了关于CAS的一些操作, 那么AtomicInteger是如何使用Unsafe来实现同步操作的呢?
AtomicInteger
方法实现
正如开头提到的,AtomicInteger正如类名,完成原子性操作,其大部分操作都是使用Unsafe类来实现的。
public class AtomicInteger extends Number implements java.io.Serializable {
//定义unsafe对象
private static final Unsafe unsafe = Unsafe.getUnsafe();
//表示value值的偏移量
private static final long valueOffset;
//静态方法获取value的偏移量并赋给valueOffset
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
注意AtomicInteger内部的代码,回顾java对象jvm(二):对象加载浅谈
,除了对象头等知识外,还有一点再此做补充:
一个类一旦定义完毕,其内部字段相对于对象的偏移量就固定了。
于是我们就可以使用类而不是对象来获取属性的偏移量了。
valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
然后就调用Unsafe的方法了,如下:
public class AtomicInteger extends Number implements java.io.Serializable {
//...属性定义
//典型的CAS操作,如果value==expect,则设置value=update
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
//设置value为newValue,并返回value
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
AtomicInteger剩下的方法都是诸如自增,自减等操作了。
同步机制
说到底其并未使用常见的synchronized或者锁机制,那AtomicInteger的同步是如何实现的呢?
秘诀就在于CAS。
部分源码如下:
public class AtomicInteger extends Number implements java.io.Serializable {
//...
//设置value为newValue,并返回value
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
//...
}
public final class Unsafe {
//...
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//...
}
假设有3个线程,同时调用对于同一个AtomicInteger的getAndAdd(1)操作,然后仔细分析上述代码,就会发现,即使中间的过程可能会存在差异,但最后该AtomicInteger一定是3.