原子累加器
JDK中提供了原子累加器类,它们的性能比AotomicLong要高很多
累加器有两个参数:
1:Supplier:提供者 无中生有()->结果 提供累加对象
2:comsumer:消费者 一个参数没有(参数)->void 操作方法
public class com {
public static void main(String[] args) {
demo(
()->new AtomicLong(0),
(adder) -> adder.getAndIncrement()
);
demo(
()->new LongAdder(),
adder -> adder.increment()
);
}
private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action){
T adder = adderSupplier.get();
List<Thread> ts = new ArrayList<>();
for (int i = 0; i < 4;i++){
ts.add(new Thread(() -> {
action.accept(adder);
}));
}
long start = System.nanoTime();
ts.forEach(t -> t.start());
ts.forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
性能提升的原理:它实际上是在竞争的时候,设置多个累加单元,线程1累加Cell[0],线程2累加Cell[1],最后将觉果汇总。这样它们在累加时操作不同的Cell变量,因此减少了CAS重试失败,从而提高性能。
LongAdder原理——cas锁
LongAdder是并发大师@author douglea(大哥李)的作品,设计的非常精巧
LongAdder类有几个关键域
原理之伪共享
LongAdder中的cells就是累加单元,它的实现如下:
其中@sun.misc.Contended这个注解的作用就防止缓存行的伪共享
那么什么是缓存行的伪共享?
这得从缓存说起,我们先来看一下缓存和内存的速度比较:
因为CPU和内存的速度差异很大,需要靠着预读数据至缓存来提升效率。
而缓存又以缓存行作为单位,每个缓存行对应着一块内存,一般是64byte。
缓存的加入会造成数据副本的产生,就是同一份数据会缓存到不同的缓存行中,一个线程对应一个CPU,对应各一份缓存。
如果CPU要保证数据的一致性,如果某一个CPU核心更改了数据,那么其他CPU核心对应的整个缓存行必须失效
所以当一个Core累加修改成功,那么另一个Core中的缓存就会失效,重新去内存中去获取新的数据,这就会带来效率上的降低。
因为失效是以缓存行为单位的,以前的方法如果失效会多个Cell同时失效,所以后面将采用不同的Cell存在不同的缓存行,那么就可以避免失效问题。
@sun.misc.Contended用来解决这个问题,它的原理是在使用此注解的对象或字段的前后各增加128个字节大小的padding(空白),从而让CPU将对象预读到缓存的时候各自占用不同的缓存行,这样就不会造成对方缓存行的失效。
源码
LongAdder.increment()的源码如下:
然后再来看看longAccumulate方法的源码
如果还没新建Cells累加单元数组,那么会执行以下代码:
如果Cells创建好了,但是当前线程的累加单元还没创建好,会执行以下代码:
如果Cells累加单元数组以及Cell累加单元都已经存在了那么执行以下代码:
最后需要将累加单元数组里的数进行综合,代码如下:
Unsafe
Unsafe对象提供了非常底层的,操作内存,线程的方法,Unsafe对象不能直接调用,只能通过反射获得
CAS和LockSupport中的park,unpark方法中底层使用的都是Unsafe对象
Unsfae对象CAS相关方法
操作示例:
使用Unsafe模拟实现原子整数xuxi
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class Test8 {
public static void main(String[] args) {
}
}
class MyAutomicInteger{
private volatile int value;
private static final long valueOffset;
private static final Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe)theUnsafe.get(null);
valueOffset = UNSAFE.objectFieldOffset(MyAutomicInteger.class.getDeclaredField("value"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int getValue(){
return value;
}
public void decrement(int amount){
while (true){
int prev = this.value;
int after = prev - amount;
if (UNSAFE.compareAndSwapInt(this,valueOffset,prev, after)){
break;
}
}
}
public void increment(int amount){
while (true){
int prev = this.value;
int after = prev + amount;
if (UNSAFE.compareAndSwapInt(this,valueOffset,prev, after)){
break;
}
}
}
}
学习笔记——素材form黑马程序员