# CAS
核心观念就是比较并替换
# 问题产生
多线程下修改金额,最终金额不是预期结果
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public class Demo {
public static void main(String[] args) {
Account account = new AccountUnsafe(10000);
Account.demo(account);
}
}
class AccountUnsafe implements Account {
private Integer balance;
public AccountUnsafe(Integer balance) {
this.balance = balance;
}
@Override
public Integer getBalance() {
synchronized (this) {
return this.balance;
}
}
@Override
public void withdraw(Integer amount) {
this.balance -= amount;
}
}
interface Account {
// 获取余额
Integer getBalance();
// 取款
void withdraw(Integer amount);
/**
* 方法内会启动 1000 个线程,每个线程做 -10 元 的操作
* 如果初始余额为 10000 那么正确的结果应当是 0
*/
static void demo(Account account) {
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
ts.add(new Thread(() -> {
account.withdraw(10);
}));
}
long start = System.nanoTime();
ts.forEach(Thread::start);
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
long end = System.nanoTime();
System.out.println(account.getBalance()
+ " cost: " + (end-start)/1000_000 + " ms");
}
}
在多线程下最总的金额并不是0,在使用无锁的情况下使用 AtomicInteger 得以解决
class AccountSafe implements Account {
private AtomicInteger balance;
public AccountSafe(int account) {
this.balance = new AtomicInteger(account);
}
@Override
public Integer getBalance() {
return balance.get();
}
@Override
public void withdraw(Integer amount) {
while (true) {
int pre = balance.get();
int next = pre - amount;
//核心操作,期望值是 pre,改变值是next
if (balance.compareAndSet(pre, next)) {
break;
}
}
}
}
# AtomicInteger
说明
三个类API都差不多,所以以AtomicInteger举例
- AtomicInteger
- AtomicBoolean
- AtomicIong
# compareAndSet
@Slf4j
public class Demo {
public static void main(String[] args) {
//无参构造就是0
AtomicInteger atomicInteger = new AtomicInteger(2);
//比较和替换,但是这个需要和while 配合比较麻烦
atomicInteger.compareAndSet(0, 1);
}
}
# incrementAndGet & getAndIncrement
public class Demo {
public static void main(String[] args) {
//无参构造就是0
AtomicInteger atomicInteger = new AtomicInteger(2);
//自增并获取
atomicInteger.incrementAndGet();
//获取并自增
atomicInteger.getAndIncrement();
}
}
# addAndGet & getAndAdd
public class Demo {
public static void main(String[] args) {
//无参构造就是0
AtomicInteger atomicInteger = new AtomicInteger(2);
//自定义增长数,先增加再获取
atomicInteger.addAndGet(5);
//先获取再增加
atomicInteger.getAndAdd(5);
System.out.println(atomicInteger.get());
}
}
# updateAndGet
public class Demo {
public static void main(String[] args) {
//无参构造就是0
AtomicInteger atomicInteger = new AtomicInteger(2);
/**
* x 原始值
* 和compareAndSet差不多,可以做比较复杂的运算
* 假如x 是 10,在 x * 10 后就是100,但是在运算的过程中,被别的线程更改了,
* 那么就修改失败了回去获取新的值再运算
*/
atomicInteger.updateAndGet(x -> x * 10);
System.out.println(atomicInteger.get());
}
}
# 原子引用
- AtomicReference:操作还是和上面一样
- AtomicMarkableReference
- AtomicStampedReference
# ABA
# 初形成对业务大致不会有影响,但是需要知道
public class ABA {
static AtomicReference<String> content = new AtomicReference<>("init");
public static void main(String[] args) {
System.out.println("main -> " + content.get());
other();
content.compareAndSet("init", "main update");
System.out.println("main —> " + content.get());
}
private static void other() {
new Thread(() -> content.set("t1 update")).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread init = new Thread(() -> content.set("init"));
init.start();
try {
init.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
# 解决办法:AtomicStampedReference 增加版本号
public class ABA {
//Stamped 本意是 时间戳得意思,在这里当作版本号
static AtomicStampedReference<String> content = new AtomicStampedReference<>("A", 0);
public static void main(String[] args) {
int stamp = content.getStamp();
System.out.println(" main -> " + content.getReference() + " version ->" + stamp);
other();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
boolean b = content.compareAndSet("A", "new B", stamp, stamp + 1);
System.out.println("update A to new B state: " + b );
}
private static void other() {
new Thread(() -> {
int stamp = content.getStamp();
content.compareAndSet("A", "B", stamp, stamp + 1);
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
int stamp = content.getStamp();
content.compareAndSet("B", "A", stamp, stamp + 1);
}).start();
}
}
# 原子数组
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
# AtomicIntegerArray
public class Demo {
public static void main(String[] args) {
demo(
() -> new int[10],
(array) -> array.length,
(array, index) -> array[index]++,
array -> System.out.println(Arrays.toString(array))
);
demo(
() -> new AtomicIntegerArray(10),//线程安全
(array) -> array.length(),
(array, index) -> array.getAndIncrement(index),
array -> System.out.println(array)
);
}
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T, Integer> lengthFun,
BiConsumer<T, Integer> putConsumer,
Consumer<T> printConsumer) {
List<Thread> ts = new ArrayList<>();
T array = arraySupplier.get();
int length = lengthFun.apply(array);
for (int i = 0; i < length; i++) {
// 每个线程对数组作 10000 次操作
ts.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j % length);
}
}));
}
ts.forEach(t -> t.start()); // 启动所有线程
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}); // 等所有线程结束
printConsumer.accept(array);
}
}
# 原子更新器
- AtomicReferenceFieldUpdater
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
@Slf4j
public class Demo {
public static void main(String[] args) {
Student student = new Student();
/**
* @params1: 哪个对象
* @params2: 属性类型
* @params3: 属性名称
*/
AtomicReferenceFieldUpdater<Student, String> name = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
boolean b = name.compareAndSet(student, null, "new name");
log.info("修改状态:{}",b);
}
}
class Student {
//必须加上volatile 不然报错
volatile String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
# 原子累加器
性能比AtomicLong 效果要高几倍
- LongAdder