JDK1.8时,java.util.concurrent.atomic包中提供了一个新的原子类:LongAdder。根据Oracle官方文档的介绍,LongAdder在高并发的场景下会比它的前辈——AtomicLong 具有更好的性能,代价是消耗更多的内存空间。AtomicLong是利用了底层的CAS操作来提供并发性的,调用了Unsafe类的getAndAddLong方法,该方法是个native方法,它的逻辑是采用自旋的方式不断更新目标值,直到更新成功。
在并发量较低的环境下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发环境下,N个线程同时进行自旋操作,会出现大量失败并不断自旋的情况,此时AtomicLong的自旋会成为瓶颈。这就是LongAdder引入的初衷——解决高并发环境下AtomicLong的自旋瓶颈问题。
AtomicLong中有个内部变量value保存着实际的long值,所有的操作都是针对该变量进行。也就是说,高并发环境下,value变量其实是一个热点,也就是N个线程竞争一个热点。
private final long value;
对于LongAdder来说,内部有一个base变量,一个Cell[]数组。
base变量:非竞态条件下,直接累加到该变量上。
Cell[]数组:竞态条件下,累加个各个线程自己的槽Cell[i]中。
transient volatile Cell[] cells;
transient volatile long base;
所以,最终结果的计算应该是:
public long sum() {
Cell[] as = cells; Cell a;
long sum = base;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
在实际运用的时候,只有从未出现过并发冲突的时候,base基数才会使用到,一旦出现了并发冲突,之后所有的操作都只针对Cell[]数组中的单元Cell。
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
而LongAdder最终结果的求和,并没有使用全局锁,返回值不是绝对准确的,因为调用这个方法时还有其他线程可能正在进行计数累加,所以只能得到某个时刻的近似值,这也就是LongAdder并不能完全替代LongAtomic的原因之一。而且从测试情况来看,线程数越多,并发操作数越大,LongAdder的优势越大,线程数较小时,AtomicLong的性能还超过了LongAdder。
除了新引入LongAdder外,还有引入了它的三个兄弟类:LongAccumulator、DoubleAdder、DoubleAccumulator。LongAccumulator是LongAdder的增强版。LongAdder只能针对数值的进行加减运算,而LongAccumulator提供了自定义的函数操作。通过LongBinaryOperator,可以自定义对入参的任意操作,并返回结果(LongBinaryOperator接收2个long作为参数,并返回1个long)。
LongAccumulator内部原理和LongAdder几乎完全一样。
DoubleAdder和DoubleAccumulator用于操作double原始类型。
StampedLock是Java8引入的一种新的锁机制,简单的理解可以认为它是读写锁的一个改进版本,读写锁虽然分离了读和写的功能,使得读与读之间可以完全并发,但是读和写之间依然是冲突的,读锁会完全阻塞写锁,它使用的依然是悲观的锁策略;如果有大量的读线程,他也有可能引起写线程的饥饿。
而StampedLock则提供了一种乐观的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞写线程。它的思想是读写锁中读不仅不阻塞读,同时也不应该阻塞写。
读不阻塞写的实现思路:
在读的时候如果发生了写,则应当重读而不是在读的时候直接阻塞写!即读写之间不会阻塞对方,但是写和写之间还是阻塞的,StampedLock的内部实现是基于CLH的。
参考代码:
public class StampedLockDemo {
// 一个点的x,y坐标
private double x,y;
// Stamped类似一个时间戳的作用,每次写的时候对其+1来改变被操作对象的Stamped值,这样其它线程读的时候发现目标对象的Stamped改变,则执行重读
private final StampedLock slock = new StampedLock();
/**
* 【写锁(排它锁)】
*/
void move (double deltaX, double deltaY) {
//tampedLock调用writeLock和unlockWrite时候都会导致stampedLock的stamp值的变化 即每次+1,直到加到最大值,然后从0重新开始
long stamp = slock.writeLock();
try {
x += deltaX;
y += deltaY;
}finally {
slock.unlockWrite(stamp);
}
}
/**
* 【乐观读锁】
*/
double distanceFromOrigin() {
// tryOptimisticRead是一个乐观的读,使用这种锁的读不阻塞写 , 每次读的时候得到一个当前的stamp值(类似时间戳的作用)
long stamp = slock.tryOptimisticRead();
// 这里就是读操作,读取x和y,因为读取x时,y可能被写了新的值,所以下面需要判断
double currentX = x, currentY = y;
/*
* 如果读取的时候发生了写,则stampedLock的stamp属性值会变化,此时需要重读,
* validate():比较当前stamp和获取乐观锁得到的stamp比较,不一致则失败。
* 再重读的时候需要加读锁(并且重读时使用的应当是悲观的读锁,即阻塞写的读锁)
* 当然重读的时候还可以使用tryOptimisticRead,此时需要结合循环了,即类似CAS方式
* 读锁又重新返回一个stampe值
*/
if(!slock.validate(stamp)) {//如果验证失败(读之前已发生写)
stamp = slock.readLock(); //悲观读锁
try {
currentX = x;
currentY = y;
}finally {
slock.unlockRead(stamp);
}
}
return Math.sqrt(currentX *currentX + currentY *currentY);
}
/**
* 读锁升级为写锁
*/
void moveIfAtOrigin(double newX, double newY) { // upgrade
// 读锁(这里可用乐观锁替代)
long stamp = slock.readLock();
try {
//循环,检查当前状态是否符合
while (x == 0.0 && y == 0.0) {
long ws = slock.tryConvertToWriteLock(stamp);
//如果写锁成功
if (ws != 0L) {
stamp = ws;// 替换stamp为写锁戳
x = newX;//修改数据
y = newY;
break;
}
//转换为写锁失败
else {
slock.unlockRead(stamp); //释放读锁
//获取写锁(必要情况下阻塞一直到获取写锁成功)
stamp = slock.writeLock();
}
}
} finally {
slock.unlock(stamp); //释放锁(可能是读/写锁)
}
}
}
Future的不足:Future是Java
5添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel方法停止任务的执行。
虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?
Java的一些框架,比如Netty,自己扩展了Java的 Future接口,提供了addListener等多个扩展方法,Google guava也提供了通用的扩展Future:ListenableFuture、SettableFuture 以及辅助类Futures等,方便异步编程。同时Future接口很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:
JDK1.8才新加入的一个实现类CompletableFuture,实现了Future, CompletionStage两个接口。实现了Future接口,意味着可以像以前一样通过阻塞或者轮询的方式获得结果。
除了直接new出一个CompletableFuture的实例,还可以通过工厂方法创建CompletableFuture的实例
工厂方法:
public static CompletableFuture runAsync(Runnable runnable) {
if (runnable == null) throw new NullPointerException();
CompletableFuture f = new CompletableFuture();
execAsync(ForkJoinPool.commonPool(), new AsyncRun(runnable, f));
return f;
}
public static CompletableFuture runAsync(Runnable runnable,、 Executor executor) {
if (executor == null || runnable == null)
throw new NullPointerException();
CompletableFuture f = new CompletableFuture();
execAsync(executor, new AsyncRun(runnable, f));
return f;
}
public static CompletableFuture supplyAsync(Supplier supplier) {
if (supplier == null) throw new NullPointerException();
CompletableFuture f = new CompletableFuture();
execAsync(ForkJoinPool.commonPool(), new AsyncSupply(supplier, f));
return f;
}
public static CompletableFuture supplyAsync(Supplier supplier, Executor executor) {
if (executor == null || supplier == null)
throw new NullPointerException();
CompletableFuture f = new CompletableFuture();
execAsync(executor, new AsyncSupply(supplier, f));
return f;
}
Asynsc表示异步,而supplyAsync与runAsync不同在与前者异步返回一个结果,后者是void,第二个函数第二个参数表示是用我们自己创建的线程池,否则采用默认的ForkJoinPool.commonPool()作为它的线程池。
public T get()
public T get(long timeout, TimeUnit unit)
public T getNow(T valueIfAbsent)
public T join()
getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。
join返回计算的结果或者抛出一个unchecked异常(CompletionException),它和get对抛出的异常的处理有些细微的区别。参见cn.enjoyedu.cha.cfdemo下CFDemo和JoinAndGet。
public static CompletableFuture allOf(CompletableFuture>... cfs)
public static CompletableFuture
allOf方法是当所有的CompletableFuture都执行完后执行计算。
anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。
参见代码:
public class AllofAnyOf {
public static void main(String[] args) throws Exception {
Random rand = new Random();
CompletableFuture future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future1完成");
return 100;
});
CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future2完成");
return "abc";
});
CompletableFuture future3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("future3完成");
return "123abc";
});
// allOf 方法是当所有的CompletableFuture都执行完后执行计算。
CompletableFuture.allOf(future1,future2,future3).thenRun(()->{
System.out.println("All done!");
});
//anyOf 方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同。
CompletableFuture
CompletionStage是一个接口,从命名上看得知是一个完成的阶段,它代表了一个特定的计算的阶段,可以同步或者异步的被完成。你可以把它看成一个计算流水线上的一个单元,并最终会产生一个最终结果,这意味着几个CompletionStage可以串联起来,一个完成的阶段可以触发下一阶段的执行,接着触发下一次,再接着触发下一次………。
1)计算可以由 Future ,Consumer 或者 Runnable 接口中的 apply,accept 或者 run等方法表示。
2)计算的执行主要有以下:
a. 默认执行。
b. 使用默认的CompletionStage的异步执行提供者异步执行,这些方法名使用someActionAsync这种格式表示。
c. 使用 Executor 提供者异步执行。这些方法同样也是someActionAsync这种格式,但是会增加一个Executor 参数。
变换类 thenApply:
public CompletableFuture thenApply
(Function super T,? extends U> fn) {
return doThenApply(fn, null);
}
public CompletableFuture thenApplyAsync
(Function super T,? extends U> fn) {
return doThenApply(fn, ForkJoinPool.commonPool());
}
public CompletableFuture thenApplyAsync
(Function super T,? extends U> fn,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenApply(fn, executor);
}
关键入参是函数式接口Function。它的入参是上一个阶段计算后的结果,返回值是经过转化后结果。
public CompletableFuture thenAccept
(Consumer super T> action) {
return doThenAccept(action, null);
}
public CompletableFuture thenAcceptAsync
(Consumer super T> action) {
return doThenAccept(action, ForkJoinPool.commonPool());
}
public CompletableFuture thenAcceptAsync
(Consumer super T> action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenAccept(action, executor);
}
关键入参是函数式接口Consumer,它的入参是上一个阶段计算后的结果, 没有返回值。
public CompletableFuture thenRun
(Runnable action) {
return doThenRun(action, null);
}
public CompletableFuture thenRunAsync
(Runnable action) {
return doThenRun(action, ForkJoinPool.commonPool());
}
public CompletableFuture thenRunAsync
(Runnable action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenRun(action, executor);
}
对上一步的计算结果不关心,执行下一个操作,入参是一个Runnable的实例,表示上一步完成后执行的操作。
public CompletableFuture thenCombine
(CompletionStage extends U> other,
BiFunction super T,? super U,? extends V> fn) {
return doThenCombine(other.toCompletableFuture(), fn, null);
}
public CompletableFuture thenCombineAsync
(CompletionStage extends U> other,
BiFunction super T,? super U,? extends V> fn) {
return doThenCombine(other.toCompletableFuture(), fn,
ForkJoinPool.commonPool());
}
public CompletableFuture thenCombineAsync
(CompletionStage extends U> other,
BiFunction super T,? super U,? extends V> fn,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenCombine(other.toCompletableFuture(), fn, executor);
}
需要上一步的处理返回值,并且other代表的CompletionStage 有返回值之后,利用这两个返回值,进行转换后返回指定类型的值。两个CompletionStage是并行执行的,它们之间并没有先后依赖顺序,other并不会等待先前的CompletableFuture执行完毕后再执行。
public CompletableFuture thenCompose
(Function super T, ? extends CompletionStage> fn) {
return doThenCompose(fn, null);
}
public CompletableFuture thenComposeAsync
(Function super T, ? extends CompletionStage> fn) {
return doThenCompose(fn, ForkJoinPool.commonPool());
}
public CompletableFuture thenComposeAsync
(Function super T, ? extends CompletionStage> fn,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenCompose(fn, executor);
}
对于Compose可以连接两个CompletableFuture,其内部处理逻辑是当第一个CompletableFuture处理没有完成时会合并成一个CompletableFuture,如果处理完成,第二个future会紧接上一个CompletableFuture进行处理。第一个CompletableFuture 的处理结果是第二个future需要的输入参数。
public CompletableFuture thenAcceptBoth
(CompletionStage extends U> other,
BiConsumer super T, ? super U> action) {
return doThenAcceptBoth(other.toCompletableFuture(), action, null);
}
public CompletableFuture thenAcceptBothAsync
(CompletionStage extends U> other,
BiConsumer super T, ? super U> action) {
return doThenAcceptBoth(other.toCompletableFuture(), action,
ForkJoinPool.commonPool());
}
public CompletableFuture thenAcceptBothAsync
(CompletionStage extends U> other,
BiConsumer super T, ? super U> action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doThenAcceptBoth(other.toCompletableFuture(), action, executor);
}
需要上一步的处理返回值,并且other代表的CompletionStage 有返回值之后,利用这两个返回值,进行消费。
public CompletableFuture runAfterBoth
(CompletionStage> other,
Runnable action) {
return doRunAfterBoth(other.toCompletableFuture(), action, null);
}
public CompletableFuture runAfterBothAsync
(CompletionStage> other,
Runnable action) {
return doRunAfterBoth(other.toCompletableFuture(), action,
ForkJoinPool.commonPool());
}
public CompletableFuture runAfterBothAsync
(CompletionStage> other,
Runnable action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doRunAfterBoth(other.toCompletableFuture(), action, executor);
}
不关心这两个CompletionStage的结果,只关心这两个CompletionStage都执行完毕,之后再进行操作Runnable。
public CompletableFuture applyToEither
(CompletionStage extends T> other,
Function super T, U> fn) {
return doApplyToEither(other.toCompletableFuture(), fn, null);
}
public CompletableFuture applyToEitherAsync
(CompletionStage extends T> other,
Function super T, U> fn) {
return doApplyToEither(other.toCompletableFuture(), fn,
ForkJoinPool.commonPool());
}
public CompletableFuture applyToEitherAsync
(CompletionStage extends T> other,
Function super T, U> fn,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doApplyToEither(other.toCompletableFuture(), fn, executor);
}
两个CompletionStage谁计算的快我就用那个CompletionStage的结果进行下一步的转化操作。现实开发场景中,总会碰到有两种渠道完成同一个事情,所以就可以调用这个方法,找一个最快的结果进行处理。
public CompletableFuture acceptEither
(CompletionStage extends T> other,
Consumer super T> action) {
return doAcceptEither(other.toCompletableFuture(), action, null);
}
public CompletableFuture acceptEitherAsync
(CompletionStage extends T> other,
Consumer super T> action) {
return doAcceptEither(other.toCompletableFuture(), action,
ForkJoinPool.commonPool());
}
public CompletableFuture acceptEitherAsync
(CompletionStage extends T> other,
Consumer super T> action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doAcceptEither(other.toCompletableFuture(), action, executor);
}
两个CompletionStage,谁计算的快,我就用那个CompletionStage的结果进行下一步的消费操作。
public CompletableFuture runAfterEither(CompletionStage> other,
Runnable action) {
return doRunAfterEither(other.toCompletableFuture(), action, null);
}
public CompletableFuture runAfterEitherAsync
(CompletionStage> other,
Runnable action) {
return doRunAfterEither(other.toCompletableFuture(), action,
ForkJoinPool.commonPool());
}
public CompletableFuture runAfterEitherAsync
(CompletionStage> other,
Runnable action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doRunAfterEither(other.toCompletableFuture(), action, executor);
}
两个CompletionStage,任何一个完成了都会执行下一步的操作(Runnable)。
public CompletableFuture exceptionally
(Function fn) {
if (fn == null) throw new NullPointerException();
CompletableFuture dst = new CompletableFuture();
ExceptionCompletion d = null;
Object r;
if ((r = result) == null) {
CompletionNode p =
new CompletionNode(d = new ExceptionCompletion
(this, fn, dst));
while ((r = result) == null) {
if (UNSAFE.compareAndSwapObject(this, COMPLETIONS,
p.next = completions, p))
break;
}
}
if (r != null && (d == null || d.compareAndSet(0, 1))) {
T t = null; Throwable ex, dx = null;
if (r instanceof AltResult) {
if ((ex = ((AltResult)r).ex) != null) {
try {
t = fn.apply(ex);
} catch (Throwable rex) {
dx = rex;
}
}
}
else {
@SuppressWarnings("unchecked") T tr = (T) r;
t = tr;
}
dst.internalComplete(t, dx);
}
helpPostComplete();
return dst;
}
当运行时出现了异常,可以通过exceptionally进行补偿。
public CompletableFuture whenComplete
(BiConsumer super T, ? super Throwable> action) {
return doWhenComplete(action, null);
}
public CompletableFuture whenCompleteAsync
(BiConsumer super T, ? super Throwable> action) {
return doWhenComplete(action, ForkJoinPool.commonPool());
}
public CompletableFuture whenCompleteAsync
(BiConsumer super T, ? super Throwable> action,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doWhenComplete(action, executor);
}
action执行完毕后它的结果返回原始的CompletableFuture的计算结果或者返回异常。所以不会对结果产生任何的作用。
public CompletableFuture handle
(BiFunction super T, Throwable, ? extends U> fn) {
return doHandle(fn, null);
}
public CompletableFuture handleAsync
(BiFunction super T, Throwable, ? extends U> fn) {
return doHandle(fn, ForkJoinPool.commonPool());
}
public CompletableFuture handleAsync
(BiFunction super T, Throwable, ? extends U> fn,
Executor executor) {
if (executor == null) throw new NullPointerException();
return doHandle(fn, executor);
}
运行完成时,对结果的处理。这里的完成时有两种情况,一种是正常执行,返回值。另外一种是遇到异常抛出造成程序的中断。
本补充章节仅为没接触过Lambda的同学快速入门和速查,更具体的Lamba的知识请自行查阅相关书籍和博客。现在我们有一个实体类,我们会对这个实体类进行操作。
我们想从一批Circle中挑选出挑选出半径为2的圆,于是我们写了一个方法
// 1、挑选出半径为2的圆
public static List getCircles(List circles){
List circleList = new ArrayList<>();
for(Circle circle :circles){
if(circle.getRadius()==2){
circleList.add(circle);
}
}
return circleList;
}
这样,无疑很不优雅,如果我们想挑选半径为3的圆,难道还要再写一个方法?于是我们考虑将选择条件进行参数化,比如根据颜色挑选出圆或者根据半径挑选出圆。
// 2.1、选择条件参数化,根据颜色挑选出圆
public static List getCircleByColor(List circles, String color){
List circleList = new ArrayList<>();
for(Circle circle :circles){
if(color.equals(circle.getColor())){
circleList.add(circle);
}
}
return circleList;
}
// 2.2、选择条件参数化,根据半径挑选出圆
public static List getCircleByRadius(List circles, int radius){
List circleList = new ArrayList<>();
for(Circle circle :circles){
if(radius==circle.getRadius()){
circleList.add(circle);
}
}
return circleList;
}
但是,这种实现,还是有问题的,1、选择条件变化了,那么相应的方法也要变,比如我们想挑选半径大于3的圆,怎么办?如果我要根据多个条件选择,怎么办?难道把所有的条件都传入吗?于是,我们考虑定义一个挑选圆的接口,程序进化到了第二歩。
进行行为参数化,定义一个接口ChoiceCircle:
static interface ChoiceCircle{
boolean getCircle(Circle circle);
}
在进行圆的挑选的方法里,我们把这个接口作为参数进行传递
/**
* 行为参数化,根据条件挑选出圆
*/
public static List getCircleByChoice(List circles, ChoiceCircle choice){
List circleList = new ArrayList<>();
for(Circle circle : circles) {
if(choice.getCircle(circle)) {
circleList.add(circle);
}
}
return circleList;
}
然后,我们只要按业务需求实现接口,并传入实现类的实例即可
/**
* 挑选圆的行为实现之一,选出红色的圆
*/
static class CircleByRed implements ChoiceCircle{
@Override
public boolean getCircle(Circle circle) {
return "Red".equals(circle.getColor());
}
}
/**
* 挑选圆的行为实现之二,选出半径为2的圆
*/
static class CircleByRadiusTwo implements ChoiceCircle{
@Override
public boolean getCircle(Circle circle) {
return circle.getRadius() == 2;
}
}
public static void service(){
List src = new ArrayList<>(); // 待处理的圆的集合
List result = getCircleByChoice(src, new CircleByRed());
List result2 = getCircleByChoice(src, new CircleByRadiusTwo());
}
这种方式可以提高灵活性,但是业务上每增加一个挑选行为, 我们就需要显式声明一个接口ChoiceCircle的实现类,于是我们可以考虑使用内部匿名类,进入第三步。
在实际使用时,我们不再声明一个接口ChoiceCircle的实现类:
public static void service() {
List src = new ArrayList<>(); // 待处理的圆的集合
// 选出半径为2的圆
List radiusTwos = getCircleByChoice(src, new ChoiceCircle() {
@Override
public boolean getCircle(Circle circle) {
return circle.getRadius() == 2;
}
});
// 选出红色的圆
List red = getCircleByChoice(src, new ChoiceCircle() {
@Override
public boolean getCircle(Circle circle) {
return "Red".equals(circle.getColor());
}
});
}
匿名内部类占用代码空间较多,而且存在着模版代码,这种情况下,Lambda表达式就可以派上用场了
public static void serviceByLambda() {
List src = new ArrayList<>(); // 待处理的圆的集合
// 选出半径为2的圆
List radiusTwos = getCircleByChoice(src,
(Circle circle) -> circle.getRadius() ==2);
// 选出红色的圆
List redCircle = getCircleByChoice(src,
circle -> "Red".equals(circle.getColor()));
}
所以可以把Lambda表达式看成匿名内部类的一个简洁写法,在语法上Lambda表达式包含三个部分,参数列表,箭头,主体,比如:
(parameters) -> expression或 (parameters) -> {statements;}
Lambda表达式用在函数式接口上,所谓函数式接口,是只定义了一个抽象方法的接口(Interface),接口中是否有默认方法,不影响。
注解@FunctionalInterface可以帮助我们在设计函数式接口时防止出错。
我们常用的Runnable,Callable都是函数式接口,JDK8中新增了几个函数式接口:
Predicate : 包含test方法,接受泛型的T,返回boolean,可以视为断言(检查)接口
Consumer : 包含accept方法,接受泛型的T,无返回,可以视为数据消费接口
Function
Supplier 包含get方法,无输入,返回T,可以视为创建一个新对象接口
UnaryOperator 扩展至Function
BiFunction
BinaryOperator 扩展至Function
BiFunction
BiPredicate
BiConsumer
包含accept方法,接受泛型的T,U,无返回,可以视为复合型数据消费接口同时还提供了一些为了防止自动装箱机制,而特意声明的原始类型特化的函数式接口,比如,在意义上,和对应的Predicate接口并没有差别。
4、函数描述符
函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符。Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。
我们可以用 () -> void代表参数列表为空,且返回void的函数。这正是Runnable接口所代表的。我们于是可以称() -> void是Runnable接口的函数描述符。
再考察Callable接口和Supplier接口
从函数描述符来看,Callable接口和Supplier接口是一样的,都是
() -> X
所以同一个Lambda可以同时用在这两个函数式接口上,比如:
Callable = () -> 33;
Supplier<> = () -> 33;
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内部的内存队列的延迟问题,而不是分布式队列。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。
据目前资料显示:应用Disruptor的知名项目有如下的一些:Storm, Camel,
Log4j2,还有目前的美团点评技术团队也有很多不少的应用,或者说有一些借鉴了它的设计机制。
Disruptor是一个高性能的线程间异步通信的框架,即在同一个JVM进程中的多线程间消息传递。
在JDK中,Java内部的队列BlockQueue的各种实现,仔细分析可以得知,队列的底层数据结构一般分成三种:数组、链表和堆,堆这里是为了实现带有优先级特性的队列暂且不考虑。
在稳定性和性能要求特别高的系统中,为了防止生产者速度过快,导致内存溢出,只能选择有界队列;同时,为了减少Java的垃圾回收对系统性能的影响,会尽量选择 Array格式的数据结构。这样筛选下来,符合条件的队列就只有ArrayBlockingQueue。但是ArrayBlockingQueue是通过加锁的方式保证线程安全,而且ArrayBlockingQueue还存在伪共享问题,这两个问题严重影响了性能。
ArrayBlockingQueue的这个伪共享问题存在于哪里呢,分析下核心的部分源码,其中最核心的三个成员变量为:
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
/** Number of elements in the queue */
int count;
是在ArrayBlockingQueue的核心enqueue和dequeue方法中经常会用到的,这三个变量很容易放到同一个缓存行中,进而产生伪共享问题。