AtomicBoolean:原子Boolean类型,常用来在程序中表示一个标志位。
AtomicInteger:原子Integer类型。
AtomicLong:原子Long类型,常用来在程序中生成唯一序列号。
AtomicReference:原子引用类型,用来以原子方式更新复杂类型。
除了这4个类,还有一些其他类,如针对数组类型的类AtomicLongArray、AtomicReferenceArray,以及用于以原子方式更新对象中的字段的类,如AtomicIntegerFieldUpdater、AtomicReferenceFieldUpdater等。
Java 8增加了几个类,在高并发统计汇总的场景中更为适合,包括LongAdder、LongAccumulator、Double-Adder和DoubleAccumulator
AtomicInteger有两个构造方法,第一个给定初始值,第二个初始值为0
可以直接获取或设置AtomicInteger中的值,方法是
之所以称为原子变量,是因为它包含一些以原子方式实现组合操作的方法
//以原子方式获取旧值并设置新值
public final int getAndSet(int newValue)
//以原子方式获取旧值并给当前值加1
public final int getAndIncrement()
//以原子方式获取旧值并给当前值减1
public final int getAndDecrement()
//以原子方式获取旧值并给当前值加delta
public final int getAndAdd(int delta)
//以原子方式给当前值加1并获取新值
public final int incrementAndGet()
//以原子方式给当前值减1并获取新值
public final int decrementAndGet()
//以原子方式给当前值加delta并获取新值
public final int addAndGet(int delta)
这些方法的实现都依赖另一个public方法:
public final boolean compareAndSet(int expece,int update)
compareAndSet是一个非常重要的方法,比较并设置,简称为CAS。
该方法有两个参数expect和update,以原子方式实现了如下功能:如果当前值等于expect,则更新为update,否则不更新,如果更新成功,返回true,否则返回false。AtomicInteger可以在程序中用作一个计数器,多个线程并发更新,也总能实现正确性。
public class Test extends Thread{
private static AtomicInteger counter = new AtomicInteger(0);
static class Visitor extends Thread {
@Override
public void run() {
for(int i = 0; i < 1000; i++) {
counter.incrementAndGet();
}
}
}
public static void main(String[]args) throws InterruptedException {
int num = 1000;
Thread[]threads = new Thread[num];
for(int i = 0; i < num; i++) {
threads[i]= new Visitor();
threads[i].start();
}
for(int i = 0; i < num; i++) {
threads[i].join();
}
System.out.println(counter.get());
}
}
AtomicInteger的使用方法是简单直接的,它的主要内部成员是 :
//它的声带有volatile,这是必须的,以保证内存可见性
private volatile int value;
它的大部分更新方法实现都类似
public final int incrementAndGet() {
for(;;) {
int current = get();
int next = current + 1;
if(compareAndSet(current,next))
return next;
}
}
代码主体是个死循环,先获取当前值current,计算期望的值next,然后调用CAS方法进行更新,如果更新没有成功,说明value被别的线程改了,则再去取最新值并尝试更新直到成功为止。
与synchronized锁相比,这种原子更新方式代表一种不同的思维方式。synchronized是悲观的,它假定更新很可能冲突,所以先获取锁,得到锁后才更新。原子变量的更新逻辑是乐观的,它假定冲突比较少,但使用CAS更新,也就是进行冲突检测,如果确实冲突了,那也没关系,继续尝试就好了。synchronized代表一种阻塞式算法,得不到锁的时候,进入锁等待队列,等待其他线程唤醒,有上下文切换开销。原子变量的更新逻辑是非阻塞式的,更新冲突的时候,它就重试,不会阻塞,不会有上下文切换开销。对于大部分比较简单的操作,无论是在低并发还是高并发情况下,这种乐观非阻塞方式的性能都远高于悲观阻塞式方式。
原子变量相对比较简单,但对于复杂一些的数据结构和算法,非阻塞方式往往难于实现和理解,幸运的是,Java并发包中已经提供了一些非阻塞容器,我们只需要会使用就可以了
除了可以实现乐观非阻塞算法之外,还可以实现悲观阻塞式算法,比如锁。实际上,Java并发包中的所有阻塞式工具、容器、算法也都是基于CAS的
public class Test extends Thread{
private AtomicInteger status = new AtomicInteger(0);
public void lock() {
while(!status.compareAndSet(0,1)) {
Thread.yield();
}
}
public void unlock() {
status.compareAndSet(1,0);
}
}
使用status表示锁的状态,0表示未锁定,1表示锁定,lock()、unlock()使用CAS方法更新,lock()只有在更新成功后才退出,实现了阻塞的效果,不过一般而言,这种阻塞方式过于消耗CPU