JDK1.5 以后开始提供java.util.concurrent.automic 包,该包中一共提供了13个类,用于简单、高效、线程安全的更新一个变量。给予类型,可以将这13个类分成以下四大部份:
原子更新基本类型类
automicBoolean:原子更新布尔类型
automicInteger:原子更新长整形
automicLong:原子更新整形
以上三个类提供方法基本一样如:
int addAndGet(int delta):以原子方式将输入的数值与实例中的值相加,并返回结果。
int getAndIncrement():以原子方式将当前值加1,返回自增前的值
实现:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerTest{
static AtomicInteger ai = new AtomicInteger(1);
public static void main(String[] args){
System.out.println(ai.getAndIncrement());
now,关注getAndIncrement的源码实现
public final int getAndIncrement(){
for(;;){
int current = get();
int next = current+1;
if(compareAndSet(current,next))
return current;
}
}
public final boolean compareAndSet(int expect,int update){
return unsafe.compareAndSwapInt(this,valueoffset,expect,update);
}
第一步取得atmicInteger 里面存储的数值,第二步对当前数值加一,关键第三步,调用compareAndSet()方法来进行原子更新操作,如果当前数值等于current,说明没有被其他线程修改,完成更新,如果不等compareAndSet()方法返回false,程序重新for循环进行compareAndSet()操作。
原子更新数组
AtomicIntegerArray:原子更新数组里元素
AtomicLongArray:原子更新长整形数组元素
AtomicReferenceArray:原子更新引用类型数组里元素
以上几个类提供的方法基本一样:
int addAndGet(int i,int delta):以原子方式将输入值与索引对应元素i想加
boolean compareAndSet(int i,int expect,int update):但前值等于预期值,则原子方式更新。
public class AtomicIntegerArrayTest {
static int[] value = new int[] { 1, 2 };
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
public static void mian(String[] args){
ai.getAndSet(0,3);
System.out.println(ai.get(0));
System.out.println(value[0]);
}
}
原子更新引用类型
AtomicReference:原子更新引用类型
AtomicReferenceFieldUpdater:院子更新引用类型里的字段
AtomicMarkableReference:原子更新带有标记位的引用类型。
下面以AtomicReference为例进行说明:
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceDemo {
static class User{
private String name;
private int id;
public User(String name,int id){
this.name = name;
this.id = id;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
public static AtomicReference<user> ar = new AtomicReference<>();
public static void main(String[] args){
User user = new User('aa',11);
ar.set(user);
User newUser = new User("bb",22);
ar.compareAndSet(user,newUser);
System.out.println(ar.get().getName());//bb
System.out.println(ar.get().getId());//22
}
}
原子更新字段类
如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:
AtomicIntegerFieldUpdater:原子更新整型字段
AtomicLongFieldUpdater:原子更新长整型字段
AtomicStampedReference:原子更新带有版本号的引用类型。
要想原子更新字段,需要两个步骤:
1.每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
2.更新类的字段(属性)必须为public volatile
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo {
//创建一个原子更新器
private static AtomicIntegerFieldUpdater<User> atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater (User.class,"old");
public static void main(String[] args){
User user = new User("Tom",15);
//原来的年龄 15
System.out.println(atomicIntegerFieldUpdater.getAndIncrement(user));
//现在的年龄 16
System.out.println(atomicIntegerFieldUpdater.get(user));
}
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 void setName(String name) {
this.name = name;
}
public int getOld() {
return old;
}
public void setOld(int old) {
this.old = old;
}
}
}
相关底层实现
JDK中有一个类Unsafe,它提供了硬件级别的原子操作,例如获取数据的内存地址偏移等等,所以这个类对开发人员来说是不安全的。而这个CAS就在这个Unsafe中。 到这里只需要知道java底层通过Unsafe这个工具类来完成原子操作,UnSafe中大部分方法都是本地方法,与操作系统关系密切。
CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时(条件),将内存值修改为B并返回true,否则条件不符合返回false。
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
//获得unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//存放变量value的内存偏移
private static final long valueOffset;
static {
try {
//通过unsafe获得value的内存偏移
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//volatile修饰的,保证了多线程之间看到的value值是同一份,后面会分析
private volatile int value;
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}