一、无锁(cas)
1.1 jdk提供了AtomicInteger类无锁实现原子操作
@Slf4j(topic = "c.Test08")
public class Test08 {
public static void main(String[] args) throws InterruptedException {
CasAcount casAcount = new CasAcount(10000);
for (int i = 0; i < 1000; i++) {
new Thread(()->{
casAcount.withdrawBlance(10);
}).start();
}
Thread.sleep(2000);
log.debug("{}",casAcount.getBlance());
}
}
class CasAcount implements Acount{
private AtomicInteger blance = new AtomicInteger();
public CasAcount(int value){
this.blance.set(value);
}
@Override
public int getBlance() {
return this.blance.get();
}
@Override
public void withdrawBlance(int count) {
while(true){
int blanceTemlate = getBlance();
int res = blanceTemlate - count;
if(this.blance.compareAndSet(blanceTemlate,res))//cas比较并设置
break;
}
}
}
interface Acount{
int getBlance();
void withdrawBlance(int count);
}
1.2 cas原理
其实CAS的底层是lock cmpxchg 指令(X86架构),在单核CPU和多核CPU下都能够保证【比较-交换】的原子性。cas是配合volatile的使用,才能比较的时候获取最新值。
二、Atomic的使用
2.1AtomicInteger和AtomicReference的使用
@Slf4j(topic = "c.Test09")
public class Test09 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
updateAndGet(atomicInteger,i->i*2);
System.out.println(atomicInteger.get());
}
public static void updateAndGet(AtomicInteger i, IntUnaryOperator intUnaryOperator){
while(true){
int pre = i.get();
int next = intUnaryOperator.applyAsInt(pre);
if(i.compareAndSet(pre,next))
break;
}
}
}
后面后接口式参数,可以自由操作。
如果要保护引用类型,需要使用AtomicReference
@Slf4j(topic = "c.Test10")
public class Test10 {
public static void main(String[] args) {
BigDecimalCas bigDecimalCas = new BigDecimalCas(new BigDecimal(20));
bigDecimalCas.withdraw(new BigDecimal(15.3));
System.out.println(bigDecimalCas.get());
}
}
class BigDecimalCas{
private AtomicReference<BigDecimal> blance;
public BigDecimalCas(BigDecimal b){
blance = new AtomicReference<>();
this.blance.set(b);
}
public void withdraw(BigDecimal b){
while(true){
BigDecimal pre = blance.get();
BigDecimal next = pre.subtract(b);
if(blance.compareAndSet(pre,next))
break;
}
}
public BigDecimal get(){
return blance.get();
}
}
2.2 关于ABA问题
cas只关注要旧值和最新的值是否一致,但不能排除A->B->A,这对于主线程是无知觉的,所以需要添加版本号,每改动,版本号就加1。(AtomicStampedReference类实现)
2.3 原子数组
函数式接口传递
- Supplier为无中生有,即()->new int[x]
- Function<T,Integer>为一个参数一个结果,即(arr)->arr.length
- Comsumer为一个参数无结果,即()->{}