其中的关键是compareAndSet,它的简称就是CAS(也有Compare And Swap的说法),它必须是原子操作。
注意 其实CAS的底层是lock cmpxchg指令(X86架构),在单核CPU和多核CPU下都能够保证【比较-交换】的原子性。
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class Test4 {
public static void main(String[] args) {
demo(
() -> new AtomicLong(0),
(adder) -> adder.getAndIncrement()
);
demo(
() -> new LongAdder(),
(adder) -> adder.increment()
);
}
/**
*
* @param adderSupplier : () -> 结果 提供累加器对象
* @param action : (参数) -> void 执行累加操作
* @param <T>
*/
private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
T adder = adderSupplier.get();
List<Thread> threadList = new ArrayList<>();
// 4个线程,每人累加50万
for (int i = 0; i < 4; i++) {
threadList.add(new Thread(() -> {
for (int j = 0; j < 500000; j++) {
action.accept(adder);
}
}));
}
long start = System.nanoTime();
threadList.forEach(t -> t.start());
long end = System.nanoTime();
System.out.println(adder + " cost : " + (end - start));
}
}
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class Test3 {
public static void main(String[] args) {
}
/**
* 参数1,提供数组,可以是线程不安全数组或线程安全数组
* 参数2,获取数组长度的方法
* 参数3,自增方法,回传array,index
* 参数4,打印数组的方法
*
* @param arraySupplier : 提供者 无中生有 () -> 结果
* @param lengthFun : 函数 一个参数一个结果 (参数) -> 结果 ,BiFunction(参数1,参数2) -> 结果
* @param putConsumer : 消费者 (参数1,参数2) -> void
* @param printConsumer : 消费者 (参数) -> void
* @param <T>
*/
private static <T> void demo(
Supplier<T> arraySupplier,
Function<T, Integer> lengthFun,
BiConsumer<T, Integer> putConsumer,
Consumer<T> printConsumer
) {
List<Thread> threadList = new ArrayList<>();
T array = arraySupplier.get();
Integer length = lengthFun.apply(array);
for (int i = 0; i < length; i++) {
// 每个线程对数组做10000次操作
threadList.add(new Thread(() -> {
for (int j = 0; j < 10000; j++) {
putConsumer.accept(array, j % length);
}
}));
}
threadList.forEach(thread -> thread.start());
}
}
输出
性能提升的原因很简单,就是在有竞争时,设置多个累加单元,Thread-0累加 Cel【0】,而Thread-1累加Cell【1】。。。最后将结果汇总。这样它们在累加时操作的不同的Cell变量,因此减少了CAS重试失败,从而提高性能。
LongAdder类有几个关键域
缓存与内存的速度比较
因为CPU与内存的速度差异很大,需要靠预读数据至缓存来提升效率。 而缓存以缓存行为单位,每个缓存行对应着一块内存,一般是64byte(8个long) 缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中,CPU要保证数据的一致性,如果某个CPU核心更改了数据其它CPU核心对应的整个缓存行必须失效
因为Cell是数组形式,在内存中是连续存储的,一个Cell为24字节(16字节的对象头和8字节的value),因此缓存行可以存下2个的Cell对象。这样问题来了 :
获取最终结果通过sun方法
概述 Unsafe对象提供了非常底层的,操作内存、线程的方法,Unsafe对象不能直接调用,只能通过反射获得