前面一篇我们学习了AtomicInteger的基本使用,为了巩固CAS我们在进而学习下AtomicIntegerFieldUpdater和AtomicReferenceFieldUpdater。
AtomicIntegerFieldUpdater
有这个一个需求:10个人同时往张三的账号里转钱,每个人转1000。
使用synchronized实现
资源类:
@Data
class BankAccount {
private String bankName = "CCB";
private String account = "62222222254512222222";
private String owner = "zhangSan";
private Integer rmb = 0;
//第一种方式使用synchronized关键字
public synchronized void transRmb() {
rmb++;
}
}
转账:
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
//第一种方式使用synchronized关键字
bankAccount.transRmb();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}, "t" + i).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " result:" + bankAccount.getRmb());
}
转账结果:
main 转账结果:10000
使用AtomicIntegerFieldUpdater实现
资源类:
@Data
class BankAccount {
private String bankName = "CCB";
private String account = "62222222254512222222";
private String owner = "zhangSan";
//跟新对象的属性必须使用volatile关键字修饰
public volatile int dollar = 0;
//第二种方式使用AtomicIntegerFieldUpdater
//使用静态方法newUpdater()创建一个更新器,设置需要跟新的类和属性
final AtomicIntegerFieldUpdater<BankAccount> moneyUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "dollar");
public void transDollar(BankAccount bankAccount, int update) {
moneyUpdater.getAndAdd(bankAccount, update);
}
}
转账:
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
//第二种方式使用AtomicIntegerFieldUpdater(以线程安全的方式操作非线程安全的对象的属性)
bankAccount.transDollar(bankAccount, 1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}, "t" + i).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " 转账结果:" + bankAccount.getDollar());
}
转账结果:
main 转账结果:10000
使用AtomicIntegerFieldUpdater需要注意的是:被修改属性需要用volatile 关键字修饰,保证其可见性。使用AtomicIntegerFieldUpdater避免了synchronized 这种重量级的锁,实现精确打击。我们点进转账核心方法getAndAdd(),最终也是调用Unsafe类的compareAndSwapInt()方法:
public boolean compareAndSet(T obj, int expect, int update) {
if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj);
return unsafe.compareAndSwapInt(obj, offset, expect, update);
}
AtomicReferenceFieldUpdater
需求:系统有一个初始化变量需要被赋值,只需要被赋值一次,如果第一次赋值成功后就不需要被赋值。
资源类:
/**
* @author tlh
* @date 2023/7/14 0:09
*/
class MyResource {
public volatile Boolean init = Boolean.FALSE;
final AtomicReferenceFieldUpdater<MyResource, Boolean> updater = AtomicReferenceFieldUpdater.newUpdater(MyResource.class, Boolean.class, "init");
public void init(MyResource resource) {
if (updater.compareAndSet(resource, Boolean.FALSE, Boolean.TRUE)) {
System.out.println(Thread.currentThread().getName() + "初始工作需要2秒钟");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "初始工作完成");
} else {
System.out.println(Thread.currentThread().getName() + " 已经有其他的线程在初始了");
}
}
}
初始化:
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) {
MyResource resource = new MyResource();
for (int i = 0; i < 10; i++) {
new Thread(() -> resource.init(resource), i + "").start();
}
}
}
初始化过程:
0初始工作需要2秒钟
4 已经有其他的线程在初始了
2 已经有其他的线程在初始了
3 已经有其他的线程在初始了
5 已经有其他的线程在初始了
6 已经有其他的线程在初始了
7 已经有其他的线程在初始了
1 已经有其他的线程在初始了
9 已经有其他的线程在初始了
8 已经有其他的线程在初始了
0初始工作完成
10个线程去初始化变量init,当线程0先进入初始化流程之后,别的线程都不能进行初始化操作。 需要注意的是被AtomicReferenceFieldUpdater操作的变量必须是一个应用类型,并且被volatile关键字修饰。 这类初始化init变量的核心方法最终也是调用Unsafe列的compareAndSwapObject()方法。
public boolean compareAndSet(T obj, V expect, V update) {
if (obj == null || obj.getClass() != tclass || cclass != null ||
(update != null && vclass != null &&
vclass != update.getClass()))
updateCheck(obj, update);
return unsafe.compareAndSwapObject(obj, offset, expect, update);
}