有些时候,我们希望对一个数值类型的操作是一个原子操作,比如一个计数器,我们希望对一个计数器++i 之后,可以拿到的i是++之后的值。
你如果是这样写 m=++i; 当前i可能是2,再执行++i之后是3,此时如果另外的线程也进行了++操作,那当赋值给m的时候,可能有一定的几率就不是3了。
要保证这样的原子操作,可以使用线程同步的方式来实现,但是使用AtomicInteger,会更方便一些。
例:
AtomicInteger atomic = new AtomicInteger(0);
int num = atomic.incrementAndGet();
incrementAndGet方法保证返回的结果是+1之后的结果。
此外它还有一系列的原子性操作:
atomic.getAndDecrement();
//num = i--;
atomic.getAndIncrement();
//num = i++;
atomic.getAndAdd(3);
//num = i; i+=3;
atomic.getAndSet(3);
//num = i; i=3;
atomic.decrementAndGet();
//num = --i;
atomic.incrementAndGet();
//num = ++i;
atomic.addAndGet(3);
//i+=3; num = i;
//atomic.set(3);
//i=3;
atomic.compareAndSet(2, 3);
//if(i==2) i=3;
这些操作,保证了对一个数值对象,进行基本操作的原子性。保证了一个线程在对一个数值进行操作之后,拿到得是自己操作后的结果,而不是别的线程操作后的结果。
在实际中使用到的场景主要是多线程下的计数器,版本号等,虽然和Lock实现(ReentrantLock)的原理都一样,都是使用了UnSafe类native的compareAndSwapInt等方法实现的,但是至少在用法上更简洁,更方便。
除了AtomicInteger之外,还有AtomicBoolean,AtomicLong。都是保证对数值类型的操作的原子性。
AtomicReference 是对一个对象引用的原子操作。
举例:
MyObject p1 = new MyObject(1);
AtomicReference pR = new AtomicReference(p1);
pR.compareAndSet(p1, new MyObject(2));
相当于:
MyObject p1 = new MyObject(1);
MyObject pR = p1;
if(pR == p1)
{
pR = new MyObject(2);
}
保证对一个对象引用的原子操作。
此外还有他们的衍生类型
AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray 保证对数组类型里的元素的操作的原子性。
AtomicIntegerFieldUpdater/AtomicLongFieldUpdater/AtomicReferenceFieldUpdater 保证对一个对象的成员的操作的原子性