AtomicInteger
- AtomicInteger 的作用是什么?
- AtomicInteger 在多线程下如何保持一致性?
- IntUnaryOperator / IntBinaryOperator 接口的作用是什么?
AtiomicInteger 的作用是什么?
Atomic是一个原子类,可以在多线程的条件下安全的实现整数操作问题
@Test
public void test1(){
AtomicInteger atomicCounter = new AtomicInteger(0);
// 使用线程池执行10次增加操作
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
atomicCounter.incrementAndGet();
}
}).start();
}
// 等待所有线程执行完毕
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终结果
System.out.println("Counter: " + atomicCounter.get());
}
AtomicInteger 在多线程下如何保持一致性?
通过查看 AtomicInteger 的源码可以发现,它实现多线程安全是通过 volatile 关键字加上 CAS 自旋实现的
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value")); // 获取字段偏移量,在进行CAS操作时,需要通过偏移量来进行校验
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value; // 实际存储的值,通过 volatile 进行修改,保证了多线程情况下的可见性和有序性
下面这个方法表示了,如何实现多线程一致性。主要是通过 CAS + 不断的循环。其他的方法与其也非常类似,只是参数、返回值不同,整个处理逻辑都是一样的
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev); // IntUnaryOperator是一个函数式接口,通过自己实现其方法为变量赋新值
} while (!compareAndSet(prev, next)); // 其中 compareAndSet 是调用了本地方法的 compareAndSet(偏移量,旧值,新值),一直进行赋值,直至成功
return prev;
}
需要注意的一点, 带 lazy 的方法则不保证可见性。如下面方法则不保证可见性
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
IntUnaryOperator / IntBinaryOperator 接口的作用是什么?
IntUnaryOperator 是一个一元函数,即只接受一个参数,返回一个结果。IntBinaryOperator 是一个二元函数,即接受两个参数返回一个结果
@Test
public void testGetAndUpdate() {
AtomicInteger atomic = new AtomicInteger(); // 不带参数,默认初始化为0
int oldValue = atomic.getAndUpdate(newValue -> newValue + 10); // 一元函数
int newValue = atomic.get();
System.out.println("oleValue is " + oldValue);
System.out.println("newValue is " + newValue);
}
@Test
public void testGetAndAccumulate() {
AtomicInteger atomic = new AtomicInteger(2); // 初始化为2
int oldValue = atomic.getAndAccumulate(20, (preValue,b)->preValue*b); // 第二个参数是一个二元函数,二元函数中的第一个参数通过 atomic.get()得到,不需要传递,第二个参数需要进行传递。
int newValue = atomic.get();
System.out.println("oleValue is " + oldValue);
System.out.println("newValue is " + newValue);
}