Skip to content

原子类

基本原子类

  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
java
// 获取当前值 
public final int get()
    
// 获取当前的值,并设置新的值
public final int getAndSet(int newValue)

// 获取当前的值并自增
public final int getAndIncrement()
    
// 获取当前的值并自减
public final int getAndDecrement()

// 获取当前的值并加上预期的值
public final int getAndAdd(int delta)
    
// CAS 输入值等于预期值,则以原子方式将新值设置入目标值
public final boolean compareAndSet(int expect, int update)

使用 CountDownLatch 计数器判断所有线程执行完毕

java
class MyNumber //资源类
{
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addPlusPlus() {
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {

    public static final int SIZE = 50;

    public static void main(String[] args) {

        MyNumber myNumber = new MyNumber();

        CountDownLatch countDownLatch = new CountDownLatch(SIZE);

        for (int i = 0; i < SIZE; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1;j <= 1000; j++) {
                        myNumber.addPlusPlus();
                    }
                }finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        
        // 采用 countDownLatch 等待所有线程执行完再执行主线程
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "result:" + myNumber.atomicInteger.get());
    }
    
}

数组原子类

  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

引用类型原子类

  • AtomicReference

原子引用类 CAS 解决线程安全

java
public static void main(String[] args) {

    AtomicReference<User> atomicReference = new AtomicReference<>();
    User z3 = new User("z3", 22);
    User li4 = new User("li4", 23);

    atomicReference.set(z3);

    //true---User(userName=li4, age=23) 第一次 CAS 修改成功
    //false---User(userName=li4, age=23) 第二次 CAS 修改失败
    System.out.println(atomicReference.compareAndSet(z3,li4) 
            + "---" + atomicReference.get().toString());
    System.out.println(atomicReference.compareAndSet(z3,li4) 
            + "---" + atomicReference.get().toString());
}
  • AtomicStampedReference

带戳记流水的原子引用类,标记版本号,解决 ABA 修改过几次的问题

java
@Data
@AllArgsConstructor
class Book{
    private int id;
    private String bookName;
}

public class AtomicStampedDemo {
    public static void main(String[] args) {
        Book javaBook = new Book(1, "javaBook");
        AtomicStampedReference<Book> stampedReference = new AtomicStampedReference<>(javaBook, 1);
        System.out.println(stampedReference.getReference().toString() + " " + stampedReference.getStamp());

        Book mysqlBook = new Book(2, "mysqlBook");

        boolean b;

        b = stampedReference.compareAndSet(javaBook, mysqlBook, stampedReference.getStamp(), stampedReference.getStamp() + 1);
        System.out.println(b + " " +stampedReference.getReference() + " " + stampedReference.getStamp());

        b = stampedReference.compareAndSet(mysqlBook, javaBook, stampedReference.getStamp(), stampedReference.getStamp() + 1);
        System.out.println(b + " " +stampedReference.getReference() + " " + stampedReference.getStamp());
    }

    //Book(id=1, bookName=javaBook) 1
    //true Book(id=2, bookName=mysqlBook) 2
    //true Book(id=1, bookName=javaBook) 3
}
  • AtomicMarkableReference

原子标记引用类型,解决 ABA 问题,是否被修改过

java
public class AtomicMarkableReferenceDemo {

    static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);

    public static void main(String[] args) {
        new Thread(()->{
            boolean marked = markableReference.isMarked();
            //t1默认标识false
            System.out.println(Thread.currentThread().getName() + "默认标识" + marked);
            TimeUnit.SECONDS.sleep(1);
            markableReference.compareAndSet(100,1000,marked,!marked);
        },"t1").start();
        
        new Thread(()->{
            boolean marked = markableReference.isMarked();
            //t2默认标识false
            System.out.println(Thread.currentThread().getName() + "默认标识" + marked);

            TimeUnit.SECONDS.sleep(2);

            // false 因为 t1线程修改过,该标识已经改变,t2 无法修改了
            boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);

            // 1000 结果为线程 1 改为的 1000
            System.out.println(markableReference.getReference());
        },"t2").start();
    }
}

对象属性修改原子类

  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater
  1. 传统方式使用 synchronized 对转账操作进行线程安全处理粒度太粗,是对象锁
  2. 使用 AtomicIntegerFieldupdater 对金额变量进行细粒度加锁,更轻量化,性能更高
java
class BankAccount {
    String bankName = "CCB";

    public volatile int money = 0;

    // 1. 传统方式 
    public synchronized void add() {
        money++;
    }

    // 2. 使用 AtomicIntegerFieldupdater 对 money 字段细粒度加锁
    public void transMoney(BankAccount bankAccount) {
        fieldUpdater.getAndIncrement(bankAccount);
    }
    
    AtomicIntegerFieldUpdater fieldUpdater = AtomicIntegerFieldUpdater
            .newUpdater(BankAccount.class, "money");
}

public static void main(String[] args) throws Exception{
    BankAccount bankAccount = new BankAccount();
    CountDownLatch countDownLatch = new CountDownLatch(10);
    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            try {
                for (int j = 0; j < 10000; j++) {
//                        bankAccount.add();
                    bankAccount.transMoney(bankAccount);
                }
            }finally {
                countDownLatch.countDown();
            }
        }).start();
    }

    countDownLatch.await();
}
  1. 使用 AtomicReferenceFieldUpdater 引用类型属性进行细粒度加锁
java
import java.util.concurrent.TimeUnit;

class MyVar { //资源类

    public volatile Boolean isInit = Boolean.FALSE;

    AtomicReferenceFieldUpdater<MyVar, Boolean> referenceFieldUpdater = 
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");

    public void init(MyVar myVar) {
        if (referenceFieldUpdater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)) {
            System.out.println(Thread.currentThread().getName() + "初始化开始,需要 2 秒");
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "初始化结束");
        } else {
            System.out.println(Thread.currentThread().getName() + "已经有其他线程在初始化了");
        }
    }
}

/**
 *  多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作
    要求只能被初始化一次,只有一个线程操作成功
 */
public static void main(String[] args) {
    MyVar myVar = new MyVar();

    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            myVar.init(myVar);
        }, "t" + i).start();
    }
}

//t2已经有其他线程在初始化了
//t1初始化开始,需要 2 秒
//t0已经有其他线程在初始化了
//t4已经有其他线程在初始化了 
//t3已经有其他线程在初始化了
//t1初始化结束

原子增强类

  • LongAdder

高并发下,比 AtomicLong 减少乐观锁重试次数,性能更高,但耗费空间也更大;低并发下和 AtomicLong 性能差不多

  • LongAccumulator

LongAdder 只能从0开始累加;LongAccumulator 可以指定一个初始值与计算逻辑

java
LongAdder longAdder = new LongAdder();
public void addLongAdder() {
    longAdder.increment();
}

LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
public void addLongAccumulator() {
    longAccumulator.accumulate(1);
}

LongAdder 原理

LongAdder 的基本原理是 分散热点 ,将 Value 值分散到一个 Cell 数组中,不同线程会命中到不同的 Cell 中,减少冲突自旋概率,从而提高并发性能

总结

  • 优先 LongAddr:写入远多于读取的高并发场景
  • 优先 AtomicLong:需要频繁读取当前值且对一致性要求较高