CompletableFuture异步编排优化代码

我们在项目开发中,有可能遇到一个接口需要调用N个服务的接口。比如用户请求获取订单信息,需要调用用户信息、商品信息、物流信息等接口,最后再汇总数据统一返回。如果使用串行的方法按照顺序挨个调用接口,这样接口的响应的速度就很慢。如果并行调用接口,同时调用就会节省很多时间。下面就介绍一个好用的异步编排工具CompletableFuture

简介

CompletableFuture 是 Java 8 中新增的一个异步编程工具类,它是基于 Future 和 CompletionStage 接口构建的,可以与 Java 8 中的 Stream API 配合使用,也能够与 Java 9 中的 Reactive Stream API 进行交互。

主要用于异步执行任务并返回结果,实现异步计算和操作组合。它提供了一种灵活、可组合的方式来实现异步计算,同时也提供了异常处理、取消、超时等特性。在CompletableFuture中,我们可以通过回调函数来处理任务的结果,也可以使用其它方法来组合多个CompletableFuture对象,以构建更复杂的异步操作流水线。

创建异步任务

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier){..}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor){..}
public static CompletableFuture<Void> runAsync(Runnable runnable){..}
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor){..}

supplyAsync

supplyAsync是创建有返回值的异步任务。它有两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法

// 有返回值,默认线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) // 带返回值的异步,可以自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

代码示例

@Test
public void test() throws ExecutionException, InterruptedException { CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 执行耗时任务,返回结果
return "使用默认线程池";
});
System.out.println(future.get());
} @Test
public void test1() throws ExecutionException, InterruptedException {
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 执行耗时任务,返回结果
return "使用自定义线程池";
},executor);
System.out.println(future.get());
}

runAsync

用于异步执行一个没有返回值的任务,有两个方法,一个是使用默认线程池(ForkJoinPool.commonPool())的方法,一个是带有自定义线程池的重载方法

// 不带返回值的异步,默认线程池
public static CompletableFuture<Void> runAsync(Runnable runnable) // 不带返回值的异步,可以自定义线程池
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)

代码示例

@Test
public void testrunAsync() throws ExecutionException, InterruptedException {
CompletableFuture<Void> runAsyncVoid = CompletableFuture.runAsync(() -> {
// 执行耗时任务,返回结果
System.out.println("使用runAsync创建异步任务");
});
System.out.println("runAsyncVoid:" + runAsyncVoid.get());
} @Test
public void testrunAsyncExecutors() throws ExecutionException, InterruptedException {
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture<Void> runAsyncVoid = CompletableFuture.runAsync(() -> {
// 执行耗时任务,返回结果
System.out.println("使用runAsync创建异步任务");
},executor);
System.out.println("runAsyncVoid:" + runAsyncVoid.get());
}

运行结果

获取任务结果

CompletableFuture提供了不同的方法来获取异步任务的结果。以下是几种常用的获取结果的方法:

get()方法

该方法用于阻塞地获取异步任务的结果,返回结果的类型是泛型参数指定的类型。

get()方法会阻塞当前线程,直到异步任务完成并返回结果,或者抛出异常。因此,在使用get()方法时需要注意处理中断和异常

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 执行异步任务,返回结果
return 42;
}); try {
int result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
// 处理异常
}

join()方法

get()方法类似,也是用于获取异步任务的结果,但是不会抛出InterruptedExceptionExecutionException异常,而是将它们包装在RuntimeException中抛出

get()方法不同,join()方法不需要显式处理异常,但如果异步任务抛出了异常,join()方法将抛出CompletionException异常并包含原始异常作为其原因。

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 执行异步任务,返回结果
return 42;
}); int result = future.join();
System.out.println("Result: " + result);

一共有如下获取结果的方法

// 这个方法用于阻塞地获取异步任务的结果。如果任务已经完成,它将返回结果;如果任务尚未完成,它将阻塞当前线程直到任务完成。如果任务抛出异常,get()方法将抛出ExecutionException异常,并将原始异常作为其getCause()方法的返回值。
public T get() throws InterruptedException, ExecutionException // 这个方法与上面的get()方法类似,但是增加了一个超时参数。它会阻塞当前线程,等待指定的时间,如果在超时时间内任务完成,则返回结果;如果超时时间到达而任务仍未完成,则抛出TimeoutException异常。
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException:
// 这个方法用于获取异步任务的结果,类似于get()方法。但是与get()方法不同的是,它不会抛出受检查异常(如InterruptedException和ExecutionException),而是将它们包装在一个运行时异常(即CompletionException)中抛出。
public T join()
// 这个方法用于在异步任务尚未完成时立即返回一个默认值。如果异步任务已经完成,则返回任务的结果;如果尚未完成,则返回指定的默认值。
public T getNow(T valueIfAbsent)
// 这个方法用于手动地将异步任务标记为已完成,并设置结果值为指定的值。如果任务已经完成或已经被取消,则返回false;否则,返回true。
public boolean complete(T value)
// 这个方法用于手动地将异步任务标记为已完成,并设置异常结果为指定的异常。如果任务已经完成或已经被取消,则返回false;否则,返回true
public boolean completeExceptionally(Throwable ex)

异步回调方法

thenRun/thenRunAsync

执行完第一个任务在执行第二个任务,第二个任务无返回值

@Test
public void thenRunTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " 第一个任务....");
return 1;
}); CompletableFuture<Void> cf2 = cf1.thenRun(() -> {
System.out.println( "第二个任务获取第一个任务的结果:" + cf1.join() );
System.out.println(Thread.currentThread() + " 第二个任务....");
}); //等待任务1执行完成
System.out.println("cf1结果->" + cf1.get());
//等待任务2执行完成
System.out.println("cf2结果->" + cf2.get());
}
// thenRunAsync 和上面的方法一致,方法名换一个即可

thenRun 和thenRunAsync区别

如果你执行第一个任务的时候,传入了一个自定义线程池:

  • 调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。
  • 调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoin线程池。

后面介绍的thenAcceptthenAcceptAsyncthenApplythenApplyAsync等,它们之间的区别也是这个

thenAccept/thenAcceptAsync

第一个任务执行完成后,执行第二个回调方法任务,会将该第一个任务的执行结果,作为参数传递到回调方法中,无返回值

@Test
public void thenAcceptTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " 第一个任务....");
return 1;
}); CompletableFuture<Void> cf2 = cf1.thenAccept((result) -> {
System.out.println( "第二个任务获取第一个任务的结果:" + result );
System.out.println(Thread.currentThread() + " 第二个任务....");
}); //等待任务1执行完成
System.out.println("cf1结果->" + cf1.get());
//等待任务2执行完成
System.out.println("cf2结果->" + cf2.get()); }
// thenAcceptAsync代码和上面一致,方法名换一个即可

thenApply/thenApplyAsync

表示第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。

@Test
public void thenApplyTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " 第一个任务....");
return 1;
}); CompletableFuture<Integer> cf2 = cf1.thenApply((result) -> {
System.out.println(Thread.currentThread() + " 第二个任务...");
result += 1;
return result;
});
System.out.println("cf1结果->" + cf1.get());
System.out.println("cf2结果->" + cf2.get()); }
// thenApplyAsync代码和上面一致,方法名换一个即可

whenComplete/whenCompleteAsync

用于在异步任务完成后执行指定的操作,并且可以访问任务的结果或异常信息,会将执行结果或者执行期间抛出的异常传递给回调方法,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常

@Test
public void whenCompleteTest() throws ExecutionException, InterruptedException { CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf1 do something....");
int a = 1/0;
return 1;
}); CompletableFuture<Integer> cf2 = cf1.whenComplete((result, e) -> { if (e != null) {
System.out.println("Task failed: " + e.getMessage());
} else {
System.out.println("Result: " + result);
} System.out.println("上个任务结果:" + result);
System.out.println("上个任务抛出异常:" + e);
System.out.println(Thread.currentThread() + " cf2 do something....");
}); }

运行结果

  • whenCompleteAsync()方法使用了默认的ForkJoinPool.commonPool()线程池来执行指定的操作。如果需要自定义线程池,可以使用带有Executor参数的重载方法。
  • whenComplete()whenCompleteAsync()方法都可以访问任务的结果或异常信息,并在任务完成后执行指定的操作。它们的区别在于任务的执行方式和线程使用情况,根据具体需求选择合适的方法。

多任务组合

thenCombine/thenAcceptBoth /runAfterBoth

这三个方法都是将两个CompletableFuture组合起来处理,将只有两个任务都正常完成时,才进行下阶段任务,通俗讲就是当任务一和任务二都完成再执行任务三

区别:

  • 「thenCombine」:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
  • 「thenAcceptBoth」: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • 「runAfterBoth」 不会把执行结果当做方法入参,且没有返回值
    @Test
public void thenCombineTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf1 do something....");
return 1;
}); CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf2 do something....");
return 2;
}); CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
System.out.println(Thread.currentThread() + " cf3 do something....");
return a + b;
}); System.out.println("cf3结果->" + cf3.get());
} @Test
public void thenAcceptBothTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf1 do something....");
return 1;
}); CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf2 do something....");
return 2;
}); CompletableFuture<Void> cf3 = cf1.thenAcceptBoth(cf2, (a, b) -> {
System.out.println(Thread.currentThread() + " cf3 do something....");
System.out.println(a + b);
}); System.out.println("cf3结果->" + cf3.get());
} @Test
public void runAfterBothTest() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf1 do something....");
return 1;
}); CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread() + " cf2 do something....");
return 2;
}); CompletableFuture<Void> cf3 = cf1.runAfterBoth(cf2, () -> {
System.out.println(Thread.currentThread() + " cf3 do something....");
}); System.out.println("cf3结果->" + cf3.get());
}

运行结果:

applyToEither/acceptEither/runAfterEither

这三个方法和上面一样也是将两个CompletableFuture组合起来处理,但是当有一个任务正常完成时,就会进行下阶段任务。

两个任务,只要有一个任务完成,就执行任务三

区别在于:

  • 「runAfterEither」:不会把执行结果当做方法入参,且没有返回值
  • 「acceptEither」: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • 「applyToEither」:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值
 @Test
public void applyToEitherTest() throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf1 do something....");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cf1 任务完成";
}); CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf2 do something....");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cf2 任务完成";
}); CompletableFuture<String> cf3 = cf1.applyToEither(cf2, (result) -> {
System.out.println("接收到" + result);
System.out.println(Thread.currentThread() + " cf3 do something....");
return "cf3 任务完成";
}); System.out.println("cf3结果->" + cf3.get());
} @Test
public void acceptEither() throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf1 do something....");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cf1 任务完成";
}); CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf2 do something....");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "cf2 任务完成";
}); CompletableFuture<Void> cf3 = cf1.acceptEither(cf2, (result) -> {
System.out.println("接收到" + result);
System.out.println(Thread.currentThread() + " cf3 do something....");
}); System.out.println("cf3结果->" + cf3.get());
} @Test
public void runAfterEither() throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf1 do something....");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf1 任务完成");
return "cf1 任务完成";
}); CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf2 do something....");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf2 任务完成");
return "cf2 任务完成";
}); CompletableFuture<Void> cf3 = cf1.runAfterEither(cf2, () -> {
System.out.println(Thread.currentThread() + " cf3 do something....");
System.out.println("cf3 任务完成");
}); System.out.println("cf3结果->" + cf3.get());
}

运行结果





allOf / anyOf

  • 「allOf」:等待所有任务完成。只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null
  • 「anyOf」:只要有一个任务完成,就执行后续的操作
// allOf
@Test
public void testCompletableAallOf() throws ExecutionException, InterruptedException {
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf1 do something....");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf1 任务完成");
return "cf1 任务完成";
}); CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf2 do something....");
int a = 1/0;
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf2 任务完成");
return "cf2 任务完成";
}); CompletableFuture<String> cf3 = CompletableFuture.supplyAsync(() -> {
try {
System.out.println(Thread.currentThread() + " cf2 do something....");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cf3 任务完成");
return "cf3 任务完成";
}); CompletableFuture<Void> cfAll = CompletableFuture.allOf(cf1, cf2, cf3);
System.out.println("cfAll结果->" + cfAll.get()); }

    @Test
public void testCompletableAnyOf() throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//开启异步任务1
CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
int result = 1 + 1;
return result;
}, executorService); //开启异步任务2
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {
int result = 1 + 2;
return result;
}, executorService); //开启异步任务3
CompletableFuture<Integer> task3 = CompletableFuture.supplyAsync(() -> {
int result = 1 + 3;
return result;
}, executorService); //任务组合
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(task, task2, task3);
//只要有一个有任务完成
Object o = anyOf.get();
System.out.println("完成的任务的结果:" + o);
}

实际应用代码

public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo(); CompletableFuture<SkuInfoEntity> infoFutrue = CompletableFuture.supplyAsync(() -> {
//1 sku基本信息
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
// 无需获取返回值
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
//2 sku图片信息
List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
// 在1之后
CompletableFuture<Void> saleAttrFuture =infoFutrue.thenAcceptAsync(res -> {
//3 获取spu销售属性组合 list
List<ItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBuSpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
},executor);
// 在1之后
CompletableFuture<Void> descFuture = infoFutrue.thenAcceptAsync(res -> {
//4 获取spu介绍
SpuInfoDescEntity spuInfo = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfo);
},executor);
// 在1之后
CompletableFuture<Void> baseAttrFuture = infoFutrue.thenAcceptAsync(res -> {
//5 获取spu规格参数信息
List<SpuItemAttrGroup> attrGroups = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroups);
}, executor); // 6.查询当前sku是否参与秒杀优惠
CompletableFuture<Void> secKillFuture = CompletableFuture.runAsync(() -> {
R skuSeckillInfo = seckillFeignService.getSkuSeckillInfo(skuId);
if (skuSeckillInfo.getCode() == 0) {
// 注意null的问题
SeckillSkuRedisTo data = skuSeckillInfo.getData(new TypeReference<SeckillSkuRedisTo>() {});
SeckillInfoVo seckillInfoVo = new SeckillInfoVo();
BeanUtils.copyProperties(data,seckillInfoVo);
skuItemVo.setSeckillInfoVo(seckillInfoVo);
}
}, executor);
// 等待所有任务都完成再返回
CompletableFuture.allOf(imageFuture,saleAttrFuture,descFuture,baseAttrFuture,secKillFuture).get();
// CompletableFuture.allOf(imageFuture,saleAttrFuture,descFuture,baseAttrFuture).get();
return skuItemVo;
}

优点

  1. 异步编程能力:CompletableFuture提供了强大的异步编程能力,可以方便地处理异步计算和组合多个异步操作。它支持链式操作、组合操作、转换操作等,使得异步编程更加灵活和便捷。

  2. 非阻塞式调用:通过使用CompletableFuture,可以实现非阻塞式的调用。在执行异步任务时,主线程不会被阻塞,可以继续执行其他任务或处理其他逻辑。

  3. 异常处理:CompletableFuture提供了丰富的异常处理机制。可以通过方法链中的异常处理方法来捕获和处理任务执行过程中产生的异常,使得异常处理更加灵活和方便。

  4. 可组合性:CompletableFuture支持多个任务之间的组合操作。可以通过方法链将多个异步任务串联起来,并在任务完成后进行后续处理。这种可组合性使得代码更加清晰、可读性更高。

  5. 超时处理:使用CompletableFuture可以方便地实现超时处理。可以设置超时时间,并在超时时间内等待任务完成,如果超时则执行相应的操作,例如返回默认值或执行备用逻辑。

  6. 并发控制:CompletableFuture提供了一些方法来控制并发执行的线程数,例如thenComposeAsync()thenCombineAsync()等方法可以指定线程池来执行任务,从而实现对并发度的控制。

  7. 可以与其他异步编程工具结合使用:CompletableFuture可以与其他异步编程工具(如RxJava、Spring Reactor等)结合使用,以实现更复杂的异步编程需求。

  8. 可以与函数式编程结合使用:CompletableFuture支持函数式编程风格,可以使用Lambda表达式和方法引用来简化代码,并提高代码的可读性和可维护性。

CompletableFuture异步优化代码的更多相关文章

  1. 使用await写异步优化代码

    使用promise: function readMsg(){ return dispatch=>{ axios.post('/msgList').then(res=>{ console.l ...

  2. 使用 CompletableFuture 异步组装数据

    使用 CompletableFuture 异步组装数据 一种快捷.优雅的异步组装数据方式 实际项目中经常遇到这种情况: 从多个表中查找到数据然后拼装成一个VO返回给前端. 这个过程有可能会非常耗时.因 ...

  3. 如何解救在异步Java代码中已检测的异常

    Java语言通过已检测异常语法所提供的静态异常检测功能非常实用,通过它程序开发人员可以用很便捷的方式表达复杂的程序流程. 实际上,如果某个函数预期将返回某种类型的数据,通过已检测异常,很容易就可以扩展 ...

  4. CompletableFuture异步编排

    什么是CompletableFuture CompletableFuture是JDK8提供的Future增强类.CompletableFuture异步任务执行线程池,默认是把异步任务都放在ForkJo ...

  5. 为duilib的MenuDemo增加消息响应,优化代码和显示效果

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/38253297 第一部分 我在前一段时间研究了怎么制作duilib的菜单, ...

  6. Webpack 4教程:为什么要优化代码

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/07/30/webpack-4-course-part ...

  7. Android 性能优化:使用 Lint 优化代码、去除多余资源

    前言 在保证代码没有功能问题,完成业务开发之余,有追求的程序员还要追求代码的规范.可维护性. 今天,以“成为优秀的程序员”为目标的拭心将和大家一起精益求精,学习使用 Lint 优化我们的代码. 什么是 ...

  8. Netty实现的一个异步Socket代码

    本人写的一个使用Netty实现的一个异步Socket代码 package test.core.nio; import com.google.common.util.concurrent.ThreadF ...

  9. 如何优化代码中大量的if/else,switch/case?

    前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单 ...

  10. 异步nodejs代码的同步样子写法样例

    异步nodejs代码的同步样子写法样例 js的异步嵌套太深代码将不好看.尤其在用node的时候这种情况会大量出现. 这里用node连接redis,做一个用户注册的简单例子来说明.例如用redis做存储 ...

随机推荐

  1. Matlab学习1

    Matlab 数据类型 数字 字符和字符串 矩阵 元胞数组 结构体 清空环境变量及命令 cls % 清除Command Windows中的所有命令 clear all % 清除Workspace*中的 ...

  2. SQL 查询 总结 【行子查询 ; 列子查询 ; 表子查询 ; 自链接 ; 内连接 ;外连接 ; 无规则链接 ……】

    简单介绍一下连接方式: 1.1.使用无连接规则连接两表  无限规则  也就简单的 select * from tableA , tableB  即得到一个笛卡尔积.  什么是 笛卡尔积 在 我的 另外 ...

  3. PRF评价

    PRF评价指标: 精确率P:预测结果正类数量占全部结果的比率: P= $\frac{TP}{TP+FP}$ TP:预测为真且实际为真,FP为预测真实际为假. 召回率R:在所有正类样本中,能回想到的比例 ...

  4. 通过ssh远程执行kubectl命令报错问题

    在使用Jenkins链接Kubernetes集群,如果Jenkins安装机器与Kubernetes Master节点不在同一台机器上面,需要使用ssh远程执行部署命令,如下: ssh root@10. ...

  5. Acunetix使用说明

    简述 Acunetix是一种应用安全性扫描工具,旨在帮助发现和修复Web应用程序中的漏洞和安全风险. Acunetix可以发现以下一些常见的安全问题: 跨站脚本攻击(XSS):通过在网页中注入恶意脚本 ...

  6. 20.1K Star!Notion的开源替代方案:AFFiNE

    Notion这款笔记软件相信很多开发者都比较熟悉了,很多读者,包括我自己都用它来记录和管理自己的笔记.今天给大家推荐一个最近比较火的开源替代方案:AFFiNE.目前该开源项目已经斩获20.1K Sta ...

  7. 关于bzoj3306(树)的一些反思

    1.加零大法好,用好没烦恼 2.不要瞎开long long 3.万物皆可变成wa 4.如果超时,试图把循环中中的东西拉到外面来

  8. 使用 RediSearch 在 Redis 中进行全文检索

    原文链接: 使用 RediSearch 在 Redis 中进行全文检索 Redis 大家肯定都不陌生了,作为一种快速.高性能的键值存储数据库,广泛应用于缓存.队列.会话存储等方面. 然而,Redis ...

  9. 知识图谱(Knowledge Graph)根本概念

    目录 知识图谱 定义 基础概念: 知识图谱构建的关键技术 知识图谱的构建 实体命名识别 知识抽取 实体统一 指代消解 知识图谱的存储 RDF和图数据库的主要特点区别 知识图谱能干什么 反欺诈 不一致性 ...

  10. 让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

    目录 1. Golang 中的"数据竞争" 2. GoPool 中的数据竞争问题 3. 让 GPT-4 来修复数据竞争问题 3.1 和 GPT-4 的第一轮沟通 3.2 和 GPT ...