JDK并发包中有一个atomic包,里面实现了一些直接使用CAS操作的线程安全的类型。
其中,最常用的一个类,应该就是AtomicInteger。你可以把它看做是一个整数。他与Integer不同的是,它是可变的,线程安全的。对其的任何操作都是CAS指令执行的。
// 取得当前值
public final int get() {
return value;
}
// 设置当前值
public final void set(int newValue) {
value = newValue;
}
// 设置新值,并返回旧值
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
// 如果当前值为expect,则设置为u
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
// 当前值+1,返回旧值
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
// 当前值-1,返回旧值
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
// 当前值增加delta,返回旧值
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
// 当前值+1,返回新值
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// 当前值-1,返回新值
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
// 当前值增加delta,返回新值
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
就内部实现来说,AtomicInteger中保存一个核心字段: private volatile int value;
它就代表了AtomicInteger的当前实际取值。此外还有一个: private static final long valueoffset;
它保存着value字段在AtomicInteger对象中的偏移量。后面你会看到,这个偏移量是实现AtomicInteger的关键。
static AtomicInteger atomicInteger = new AtomicInteger();
public static void main(String[] args) {
AtomicTest atomicTest = new AtomicTest();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
System.out.println("create " + t);
return t;
}
});
for (int i = 0; i < 5; i++) {
poolExecutor.execute(atomicTest);
}
try {
Thread.sleep(80000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger);
}
public static class AtomicTest implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
atomicInteger.incrementAndGet();
}
}
}
上段代码执行完毕,结果是50000,如果是线程不安全的话,得出的结果肯定要比50000小。
我们来分析一下incrementAndGet()的内部实现(JDK1.7中)。
public final int incrementAndGet(){
for(;;){
int current = get();
int next = current + 1;
if(compareAndSet(current,next)){
return next;
}
}
}
因为CAS操作不是一定成功的,所以进行无限循环。+1后得到新的值 next,使用 compareAndSet将新值成功写入之后,return;成功的写入条件是,在写入的时刻,当前的值,应该要等于刚刚取得的current。如果不是这样,说明被别的线程修改过。要下一次尝试。