1.简介
Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作,其中包括:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。其底层就是volatile和CAS 共同作用的结果:
- volatile 保证了内存可见性。
- CAS(compare-and-swap)算法保证了原子性。 其中CAS算法的原理就是里面包含三个值:内存值A 预估值V 更新值 B 当且仅当 V == A 时,V = B; 否则,不会执行任何操作。
2.类型
- 基本类型:AtomicBoolean、AtomicInteger、AtomicLong 通过原子的方式操作布尔、整型、长整型等基本类型
- 数组:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray 通过原子的方式操作整型、长整型、引用类型等数组
- 引用类型 AtomicReference、AtomicMarkableReference、AtomicReferenceFieldUpdater 通过原子的方式操作引用类型,带标记的引用类型,引用类型里的字段
- 字段类 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference
2.1 基本类型
基本类型AtomicInteger常用的一些方法
//以原子方式将给定值与当前值相加。实际上就是等于线程安全版本的i =i+delta操作
int addAndGet(int delta)
//如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功就返回true,否则返回false,并且不修改原值。
boolean compareAndSet(int expect, int update)
//以原子方式将当前值减 1。 相当于线程安全版本的--i操作。
int decrementAndGet()
//获取当前值。
int get()
// 以原子方式将给定值与当前值相加。 相当于线程安全版本的t=i;i+=delta;return t;操作。
int getAndAdd(int delta)
// 以原子方式将当前值减 1。 相当于线程安全版本的i--操作。 int getAndDecrement()
//以原子方式将当前值加 1。 相当于线程安全版本的i++操作。
int getAndIncrement()
//以原子方式设置为给定值,并返回旧值。 相当于线程安全版本的t=i;i=newValue;return t;操作。
int getAndSet(int newValue)
// 以原子方式将当前值加 1。 相当于线程安全版本的++i操作。
int incrementAndGet()
// 最后设置为给定值。 延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。如果还是难以理解,这里就类似于启动一个后台线程如执行修改新值的任务,原线程就不等待修改结果立即返回(这种解释其实是不正确的,但是可以这么理解)。
void lazySet(int newValue)
//设置为给定值。 直接修改原始值,也就是i=newValue操作。
void set(int newValue)
//如果当前值 == 预期值,则以原子方式将该设置为给定的更新值。JSR规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen-before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JSR规范的要求,最后效果和compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。
boolean weakCompareAndSet(int expect, int update)
例子
package com.demo.common.concurrent.atomic;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest {
@Test
public void testAtomicInteger() {
final AtomicInteger atomicInteger = new AtomicInteger(10);
Assert.assertEquals(atomicInteger.compareAndSet(1, 2), false);
Assert.assertEquals(atomicInteger.compareAndSet(10, 20), true);
Assert.assertEquals(atomicInteger.get(), 20);
Assert.assertEquals(atomicInteger.incrementAndGet(), 21);
Assert.assertEquals(atomicInteger.decrementAndGet(), 20);
Assert.assertEquals(atomicInteger.getAndIncrement(), 20);
Assert.assertEquals(atomicInteger.getAndDecrement(), 21);
}
@Test
public void testMultiThread() {
final AtomicInteger atomicInteger = new AtomicInteger();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 500; i++) {
threads.add(new Thread(new AtomicIntegerThread(atomicInteger)));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Assert.assertEquals( atomicInteger.get(), 500);
}
class AtomicIntegerThread implements Runnable {
private AtomicInteger atomicInteger;
public AtomicIntegerThread(AtomicInteger atomicInteger) {
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
int preVal = atomicInteger.get();
try {
Thread.sleep(preVal % 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
int val = atomicInteger.incrementAndGet();
System.out.println(preVal + " ########### " + val);
}
}
}
2.2 数组
AtomicIntegerArray常用方法
//以原子方式将输入值与数组中索引i的元素相加。
int addAndGet(int i, int delta)
//如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值
boolean compareAndSet(int i, int expect, int update)。
2.3 引用类型
AtomicReference参考示例
public class AtomicReferenceTest {
public static AtomicReference<user> atomicUserRef = new AtomicReference</user><user>();
public static void main(String[] args) {
User user = new User("conan", 15);
atomicUserRef.set(user);
User updateUser = new User("Shinichi", 17);
atomicUserRef.compareAndSet(user, updateUser);
System.out.println(atomicUserRef.get().getName());
System.out.println(atomicUserRef.get().getOld());
}
static class User {
private String name;
private int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
2.4 字段类
AtomicIntegerFieldUpdater/AtomicLongFieldUpdater/AtomicReferenceFieldUpdater<T,V>是基于反射的原子更新字段的值。
- 字段必须是volatile类型的!在后面的章节中会详细说明为什么必须是volatile,volatile到底是个什么东西。
- 字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
- 只能是实例变量,不能是类变量,也就是说不能加static关键字。
- 只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
- 对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。
AtomicIntegerFieldUpdater的例子代码如下:
public class AtomicIntegerFieldUpdaterTest {
private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
.newUpdater(User.class, "old");
public static void main(String[] args) {
User conan = new User("conan", 10);
System.out.println(a.getAndIncrement(conan));
System.out.println(a.get(conan));
}
public static class User {
private String name;
public volatile int old;
public User(String name, int old) {
this.name = name;
this.old = old;
}
public String getName() {
return name;
}
public int getOld() {
return old;
}
}
}
2.4 ABA问题
AtomicMarkableReference和AtomicStampedReference解决
参考
微信公众号:熊英的小屋,欢迎关注。

扫描二维码关注“熊英的小屋” , 这里永远有一个位置为你开放