无锁类的原理详解
CAS
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
CAS操作是原子性的,由一条CPU指令完成
CPU指令
无锁类的使用
AtomicInteger 继承Number类
概述
主要接口
public final int get() //取得当前值
public final void set(int newValue) //设置当前值
public final int getAndSet(int newValue) //设置新值,并返回旧值
public final boolean compareAndSet(int expect, int u) //如果当前值为expect,则设置为u
public final int getAndIncrement() //当前值加1,返回旧值
public final int getAndDecrement() //当前值减1,返回旧值
public final int getAndAdd(int delta) //当前值增加delta,返回旧值
public final int incrementAndGet() //当前值加1,返回新值
public final int decrementAndGet() //当前值减1,返回新值
public final int addAndGet(int delta) //当前值加delta,返回新值
主要接口的实现
Unsafe
概述
非安全的操作,比如:
根据偏移量设置值
park()
底层的CAS操作
非公开API,在不同版本的JDK中,可能有较大差异
主要接口
//获得给定对象偏移量上的int值
public native int getInt(Object o, long offset);
//设置给定对象偏移量上的int值
public native void putInt(Object o, long offset, int x);
//获得字段在对象中的偏移量
public native long objectFieldOffset(Field f);
//设置给定对象的int值,使用volatile语义
public native putIntVolatile(Object o,long offset, int x);
//获得给定对象对象的int值,使用volatile语义
public native int getIntVolatile(Object o,long offset);
//和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的
public native void putOrderedInt(Object o, long offset, int x);
AtomicReference
概述
对引用进行修改
是一个模板类,抽象化了数据类型
主要接口
public class AtomicIntegerDemo {
static AtomicInteger i = AtomicInteger();
public static class AddThread implements Runnable{
public void run(){
for(int k = 0; k < 10000; k++)
i.incrementAndGet();
}
}
public static void main() throws InterruptedException {
Thread[] ts = new Thread[10];
for(int k = 0; k < 10; k++){
ts[k] = new Thread(new AddThread());
}
for(int k = 0; k < 10; k++){ts[k].start();}
for(int k = 0; k < 10; k++){ts[k].join();}
System.out.println(i);
}
}
原子引用保证线程安全
public class AtomicReferenceTest {
public final static AtomicReference<String> atomicStr = new AtomicReference<String>();
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
final int num = i;
new Thread() {
public void run() {
try {
Thread.sleep(Math.abs((int)(Math.random()*100)));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(atomicStr.compareAndSet("abc","def")){
System.out.println("Thread:"+Thread.currentThread().getId()+"Change value to def");
} else {
System.out.println("Thread:"+Thread.currentThread().getId()+"FAIlED");
}
}
}.start();
}
}
}
AtomicStampedReference
概述
解决ABA问题
主要接口
//比较设置参数依次为:期望值 写入新值 期望时间戳 新时间戳
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp )
//获得当前对象引用
public V getReference()
//获得当前时间戳
public int getStamp()
//设置当前对象引用和时间戳
public void set(V newReference, int newStamp)
public class AtomicStampedReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19,0);
public static void main(String[] args) {
//模拟多个线程同时更新后台数据库,为用户充值
for (int i = 0; i < 3; i++) {
final int timestamp = money.getStamp();
new Thread() {
public void run(){
while (true) {
while (true) {
Integer m = money.getReference();
if (m < 20) {
if (money.compareAndSet(m, m+20, timestamp, timestamp + 1))
System.out.println("余额小于20元,充值成功,余额:"
+ money.getReference()+ "元");
break;
}
} else {
//System.out.println("余额大于20元,无需充值");
break;
}
}
}
}
}.start();
}
// 用户消费线程,模拟消费行为
new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
while (true) {
int timestamp = money.getStamp();
Integer m = money.getReference();
if (m > 10) {
System.out.println("大于10元");
if (money.compareAndSet(m, m - 10, timestamp, timestamp + 1)) {
System.out.println("成功消费10元,余额:" + money.getReference());
break;
}
} else {
System.out.println("没有足够的金额");
break;
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
}.start();
}
}
AtomicIntegerArray
概述
支持无锁的数组
主要接口
//获得数组第i个下标的元素
public final int get(int i)
//获得数组的长度
public final int length()
//将数组第i个下标设置为newValue,并返回旧的值
public final int getAndSet(int i, int newValue)
//进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true
public final boolean compareAndSet(int i,int expect,int update)
//将第i个下标的元素加1
public final int getAndIncrement(int i)
//将第i个下标的元素减1
public final int getAndDecrement(int i)
//将第i个下标的元素增加delta(delta可以是负数)
public final int getAndAdd(int i,int delta)
public class AtomicIntegerArrayDemo {
static AtomicIntegerArray arr = new AtomicIntegerArray(10); //10个线程安全的数据
public static class AddThread implements Runnable{
public void run(){
for(int k = 0; k < 100000; k++)
//我们开了10个线程,对数组中每一个下标所在元素进行加1操作
arr.getAndIncrement(k%arr.length()); }
}
public static void main(String[] args) throws InterruptedException {
Thread[] ts = new Thread[10];
for(int k = 0; k < 10; k++){
ts[k] = new Thread(new AddThread());
}
//开了10个线程
for(int k = 0; k < 10; k++){ts[k].start();}
for(int k = 0; k < 10; k++){ts[k].join();}
System.out.println(arr);
}
}
AutomicIntegerFieldUpdater
概述
让普通变量也享受原子操作
主要接口
AutomicIntegerFieldUpdater.newUpdater()//新建一个更新器
incrementAndGet()//对数据进行更新
小说明
1.Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。比如如果score申明为private,就是不可行的。
2.为了确保变量被正确的读取,它必须是volatile类型的,如果我们原有代码中未申明这个类型,那么简单的申明一下就行,这不会引起什么问题
3.由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe,objectFieldOffset偏移量)
public class AtomicIntegerFieldUpdaterDemo {
public static class Cadidate{
int id;
volatile int score;
}
public final static AtomicIntegerFieldUpdater<Cadidate> scoreUpdater
= AtomicIntegerFieldUpdater.newUpdater(Cadidate.class, "score");
//检查Updater是否工作正确
public static AtomicInteger allScore = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
final Cadidate stu = new Candidate();
Thread[] t = new Thread[1000];
for (int i = 0 ; i < 1000 ; i++){
t[i] = new Thread(){
public void run(){
if (Math.random()>0.4){
scoreUpdater.incrementAndGet(stu);
allScore.incrementAndGet();
}
}
};
t[i].start();
}
for (int i = 0 ; i < 10000 ; i++) { t[i].join();}
System.out.println("score="+stu.score);
System.out.println("allScore="+allScore);
}
}
Console:
score=5994
allScore=5994
有锁Vector
无锁的Vector实现
LockFreeVector