Java 并发 —— CompletableFuture处理异步操作

Java 并发 —— CompletableFuture处理异步操作

简介

CompletableFuture 四种静态方法

  • 四种静态方法源码
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池异步代码。

示例

基础示例

使用 CompletableFuture.supplyAsync() 定义要执行的异步任务

public class ThreadTest {
    public static void main(String[] args) {
        /**
         * CompletableFuture.supplyAsync(),定义要执行的异步任务
         */
        ExecutorService executor = Executors.newFixedThreadPool(10);
        CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                // TODO Auto-generated method stub
                try {
                    System.out.println("任务1");
                    // 睡眠1s
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("任务2");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return "返回数据async";
            }
        }, executor);
        // return
        System.out.println("任务执行完成");
        executor.shutdown();
    }
}

基础示例2

示例1可以做到异步执行,但我们在实际开发中,会使用到异步执行,并返回执行情况

public class ThreadTest {

    public static void main(String[] args) {
        /**
         * 异步返回数据的处理方式
         */
        ExecutorService executor = Executors.newFixedThreadPool(10);

        // CompletableFuture.supplyAsync(),定义要执行的异步任务
        CompletableFuture<String> asyncResult = CompletableFuture.supplyAsync(new Supplier<String>() {

            @Override
            public String get() {
                // TODO Auto-generated method stub
                try {
                    System.out.println("任务1");
                    // 睡眠1s
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("任务2");
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return "返回数据async";
            }
        }, executor);

        // asyncResult.thenAccept(new Consumer<String>() , 重写accept()方法去定义回调函数
        asyncResult.thenAccept(new Consumer<String>() {
            @Override
            public void accept(String arg0) {
                System.out.println("return =" + arg0);
            }
        });
        // return
        System.out.println("任务执行完成");
        executor.shutdown();

    }

}

项目实战

需求

  • 定义一个需要异步执行函数的Service,在需要使用异步执行的函数上添加注释,使其支持异步执行
  • 定义一个调用异步函数的Service
  • 定义一个测试类,并进行测试

注意事项:

  • 启动类要添加注释@EnableAsync,使其支持异步调用
  • 在需要使用异步执行的函数上添加注解@Async

核心源码

  • 启动类
@SpringBootApplication
@MapperScan("com.bdjr.reporting.dao.mapper")
@ComponentScan("com.bdjr.reporting")
// 使用异步调用时,要加入EnableAsync注解
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • 供调用的异步函数的Service实现类
@Service("asynchronousService")
public class AsynchronousServiceImpl implements AsynchronousService {

    @Transactional
    @Override
    @Async
    public Future<String>  asyncAfterOne(){
        System.err.println("asyncAfterOne任务以执行");
        return new AsyncResult<String>("asyncAfterOne执行完毕");
    }

    @Transactional
    @Override
    @Async
    public Future<String> asyncAfterTwo() throws InterruptedException {
        long currentTimeMillis = System.currentTimeMillis();
        Thread.sleep(3000);
        long currentTimeMillis1 = System.currentTimeMillis();
        System.err.println("asyncAfterTwo任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
        return new AsyncResult<String>("asyncAfterTwo执行完毕");
    }

    @Transactional
    @Override
    @Async
    public Future<String> asyncAfterThree() throws InterruptedException {
        long currentTimeMillis = System.currentTimeMillis();
        Thread.sleep(6000);
        long currentTimeMillis1 = System.currentTimeMillis();
        System.err.println("asyncAfterThree任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
        return new AsyncResult<String>("asyncAfterThree执行完毕");
    }
}

  • 调用异步函数的Service实现类
@Service("tuneUpService")
public class TuneUpServiceImpl implements TuneUpService {

    @Autowired
    private AsynchronousService asynchronousService;

    @Override
    public String asyncOne() throws InterruptedException {
        long currentTimeMillis = System.currentTimeMillis();
        asynchronousService.asyncAfterOne();
        asynchronousService.asyncAfterTwo();
        asynchronousService.asyncAfterThree();
        long currentTimeMillis1 = System.currentTimeMillis();
        return "asyncAfter任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";
    }

    @Override
    public String asyncTwo() throws InterruptedException, ExecutionException {
        long currentTimeMillis = System.currentTimeMillis();
        Future<String> two = asynchronousService.asyncAfterTwo();
        Future<String> three = asynchronousService.asyncAfterThree();
        String result = null;
        while (true) {
            if(two.isDone() && three.isDone()) {
                System.err.println(two.get());
                System.err.println(three.get());
                // 两个任务都调用完成,跳出循环
                break;
            }
            Thread.sleep(1000);
        }
        long currentTimeMillis1 = System.currentTimeMillis();
        result = "asyncAfter任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";
        return result;
    }

}

单元测试

@RunWith(SpringRunner.class)
/** 标记启动类程序 **/
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableAutoConfiguration
public class TuneUpServiceImplTest {

	/** 
	 * 声明Service实现类
	 */
     @Autowired
    private TuneUpService tuneUpService;

    @Test
    public void asyncOne() throws InterruptedException {
        System.err.println(tuneUpService.asyncOne());
        Thread.sleep(8000);
    }

    @Test
    public void asyncTwo() throws InterruptedException, ExecutionException {
        System.err.println(tuneUpService.asyncTwo());
    }
}

20种CompletableFuture使用方法

1. 一个完整的CompletableFuture

  • 最简单的例子,使用一个预定义的结果创建一个完整的CompletableFuture,通常我们会在计算的开始阶段使用它。
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("异步执行成功");
        } else {
        	System.out.println("异步执行失败");
        }
    }

    /**
     * @param message 自定义信息
     * @param returnMessage CompletableFuture响应信息,即我们执行字符"我是响应信息"
     */
    public static void assertEquals(String message, Object returnMessage){
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    static void completedFutureExample() {
        // 直接执行一个异步程序,这里打印出一句话"我是响应信息"
        CompletableFuture cf = CompletableFuture.completedFuture("我是响应信息");
        // 调用某个函数,校验是否成功
        assertTrue(cf.isDone());
        // 成功后调用校验函数
        assertEquals("我定义的信息", cf.getNow(null));
    }

    public static void main(String[] args) {
        completedFutureExample();
    }
}

getNow(null)方法在future完成的情况下会返回结果,就比如上面这个例子,否则返回null (传入的参数)。

2. 简单的异步执行

  • 示例说明一个简单的异步执行情况
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("True 异步执行成功");
        } else {
            System.out.println("True 异步执行失败");
        }
    }

    public static void assertFalse(boolean done){
        if(done) {
            System.out.println("False 执行完成");
        } else {
            System.out.println("False 执行未失败");
        }
    }

    public static void sleepEnough(){
        try {
            // 睡了2秒
            TimeUnit.SECONDS.sleep(5);
            System.out.println("睡了5秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置睡眠时间
     */
    public static void randomSleep() {
        try {
            // 睡了3秒
            TimeUnit.SECONDS.sleep(3);
            System.out.println("睡了3秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static void runAsyncExample() {
        CompletableFuture cf = CompletableFuture.runAsync(() -> {
            // 首次进入,通过assertTrue判断函数是否调用了
            assertTrue(Thread.currentThread().isDaemon());
            randomSleep();
        });
        // 正在执行时,还没执行完,调用assertFalse()判断执行情况为false
        assertFalse(cf.isDone());
        // 判断randomSleep异步函数是否在sleepEnough规定的时间内执行完毕
        sleepEnough();
        // 执行完了,调用assertTrue()判断异步函数randomSleep执行情况
        assertTrue(cf.isDone());
    }

    public static void main(String[] args) {
        runAsyncExample();
    }
}
  • CompletableFuture的方法如果以Async结尾,它会异步的执行(没有指定executor的情况下), 异步执行通过ForkJoinPool实现, 它使用守护线程去执行任务。
  • 注意这是CompletableFuture的特性, 其它CompletionStage可以override这个默认的行为。

3. 基于1进行执行前修改

  • 使用1的完成的CompletableFuture,返回结果为"message",通过一个函数将返回结果转换为大写。
public class FutureTest {

    public static void assertFalse(boolean done){
        if(done) {
            System.out.println("执行完成");
        } else {
            System.out.println("执行未完成");
        }
    }

    /**
     * @param message 自定义信息
     * @param returnMessage CompletableFuture响应信息,即我们执行字符"MESSAGE"
     */
    public static void assertEquals(String message, Object returnMessage){
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致:" + "自定义信息:" + message + " ----- 异步函数响应信息:" + returnMessage);
        } else {
            System.out.println("响应信息不一致");
        }
    }

    static void thenApplyExample() {
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> {
            assertFalse(Thread.currentThread().isDaemon());
            // 将响应信息转换为大写
            return s.toUpperCase();
        });
        assertEquals("MESSAGE", cf.getNow(null));
        assertFalse(cf.isDone());
    }

    public static void main(String[] args) {
        thenApplyExample();
    }
}
  • thenApply方法名称代表的行为。
    • then 当前的阶段正常完成之后。本例中,当前节点完成,返回字符串message。
  • Apply 返回阶段会对结果前一阶段的结果应用一个函数。
  • 函数的执行会被阻塞,这意味着getNow()只有打斜操作被完成后才返回。

4. 前一阶段使用异步函数

  • 通过调用异步方法(方法后边加Async后缀),串联起来的CompletableFuture可以异步地执行(使用ForkJoinPool.commonPool())。
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("True 异步执行完成");
        } else {
            System.out.println("True 异步执行未完成");
        }
    }


    /**
     * 设置睡眠时间
     */
    public static void randomSleep() {
        try {
            // 睡了1秒
            TimeUnit.SECONDS.sleep(1);
            System.out.println("睡了1秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void assertEquals(String message, Object returnMessage){
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    public static void assertNull(Object message) {
        System.out.println("异步响应信息为 :" + message);
    }

    static void thenApplyAsyncExample() {
        // 定义异步执行行为thenApplyAsync
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
            // 校验函数调用情况
            assertTrue(Thread.currentThread().isDaemon());
            // 先睡1秒
            randomSleep();
            // 将结果转换为大写
            return s.toUpperCase();
        });
        // 输出异步函数执行后响应信息,因为睡了1秒,所以这里显示未完成
        assertNull(cf.getNow(null));
        // 判断响应结果是否与自定义"MESSAGE"一致
        assertEquals("MESSAGE", cf.join());
        // 输出异步函数执行后响应信息,这里睡眠结束后执行,所以结果是已经执行完毕了
        assertNull(cf.getNow(null));
    }

    public static void main(String[] args) {
        thenApplyAsyncExample();
    }
}

5. 使用定制Executor在前一个阶段异步应用函数

  • 异步方法非常重要特性就之一,提供Executor来异步地执行CompletableFuture。
  • 以下例子演示如何使用固定大小线程池应用大写函数。
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("True 异步执行完成");
        } else {
            System.out.println("True 异步执行未完成");
        }
    }

    public static void assertFalse(boolean done){
        if(done) {
            System.out.println("False 异步执行完成");
        } else {
            System.out.println("False 异步执行未完成");
        }
    }

    /**
     * 设置睡眠时间
     */
    public static void randomSleep() {
        try {
            // 睡了1秒
            TimeUnit.SECONDS.sleep(1);
            System.out.println("睡了1秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void assertEquals(String message, Object returnMessage){
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    public static void assertNull(Object message) {
        System.out.println("异步响应信息为 :" + message);
    }

    /**
     * 定义一个线程数为3的线程池,供调用方使用
     */
    static ExecutorService executor = Executors.newFixedThreadPool(3, new ThreadFactory() {
        int count = 1;

        @Override
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "custom-executor-" + count++);
        }
    });


    static void thenApplyAsyncWithExecutorExample() {
        // 定义一个异步执行语句"message",并使用thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)函数,注意第二个参数,使用线程池控制,意味着此异步函数有线程约束
        CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
            assertTrue(Thread.currentThread().getName().startsWith("custom-executor-"));
            assertFalse(Thread.currentThread().isDaemon());
            randomSleep();
            return s.toUpperCase();
        }, executor);
        assertNull(cf.getNow(null));
        assertEquals("MESSAGE", cf.join());
    }

    public static void main(String[] args) {
        thenApplyAsyncWithExecutorExample();
    }
}

6. 消费前一阶段结果

  • 下一阶段接收了当前阶段结果,但是计算时,不需要返回值(void),那么它就可不应用一个函数,而是一个消费者(这里指自定义StringBuffer),调用方法即变为thenAccept。
public class FutureTest {

    public static void assertTrue(String message, boolean done){
        if(done) {
            System.out.println(message + " ---- True 异步执行完成");
        } else {
            System.out.println(message + " ---- True 异步执行未完成");
        }
    }
    
    static void thenAcceptExample() {
        StringBuilder result = new StringBuilder();
        // "thenAccept message"作为函数返回值,使用void thenAccept函数调用,直接将结果应用在一个自定义消费者(StringBuffer)上。
        CompletableFuture.completedFuture("thenAccept message")
                .thenAccept(s -> result.append(s));
        // 打印函数响应信息
        System.out.println("result : " + result);
        // 进入assertTrue判断函数是否响应
        assertTrue("Result was empty", result.length() > 0);
    }

    public static void main(String[] args) {
        thenAcceptExample();
    }
}

7. 异步消费前一阶段的结果

  • 同样,可使用thenAcceptAsync方法, 串联的CompletableFuture可以异步地执行。
public class FutureTest {

    public static void assertTrue(String message, boolean done){
        if(done) {
            System.out.println(message + " ---- True 异步执行完成");
        } else {
            System.out.println(message + " ---- True 异步执行未完成");
        }
    }

    static void thenAcceptExample() {
        StringBuilder result = new StringBuilder();
        // 通过Async异步执行函数
        CompletableFuture cf = CompletableFuture.completedFuture("thenAcceptAsync message")
                .thenAcceptAsync(s -> result.append(s));
        cf.join();
        System.out.println("result : " + result);
        assertTrue("Result was empty", result.length() > 0);
    }

    public static void main(String[] args) {
        thenAcceptExample();
    }
}

8. 异步操作的异常处理

  • 异步操作如何显示异常信息
  • 简单的示例,操作处理一个字符串,把它转换成答谢,我们模拟延迟一秒。
  • JDK9的新特性,使用CompletableFuture.delayedExecutor实现
// 后续补充

9. 取消计算

  • JDK9的新特性,使用CompletableFuture.delayedExecutor实现
// 后续补充

10. 在两个完成的阶段其中之一上应用函数

  • JDK9的新特性,使用delayedUpperCase实现
// 后续补充

11. 在两个完成的阶段其中之一上调用消费函数

  • JDK9的新特性,使用delayedUpperCase实现
// 后续补充

12. 在两个阶段都执行完后运行一个 Runnable

  • 例子主要演示依赖CompletableFuture的两个阶段函数执行完后执行一个Runnable。
  • 下面阶段所有都是同步执行的,第一阶段执行大写转换,第二阶段执行执行小写转换。
public class FutureTest {

    public static void assertTrue(String message, boolean done){
        if(done) {
            System.out.println(message + " ---- True 异步执行完成");
        } else {
            System.out.println(message + " ---- True 异步执行未完成");
        }
    }

    static void runAfterBothExample() {
        String original = "Message";
        StringBuilder result = new StringBuilder();
        CompletableFuture.
                // 第一段执行
                completedFuture(original).
                    // 同步执行,将original转换为大写
                    thenApply(String::toUpperCase).
                        // 执行完后执行,第二段执行
                        runAfterBoth(
                                // 第二段执行内容
                                CompletableFuture.
                                        // 依然是自定义函数
                                        completedFuture(original).
                                            // 同步执行,将original转换为小写
                                            thenApply(String::toLowerCase) ,() ->
                                                // 定义输出结果
                                                result.append("我执行完毕了")
                        );
        System.out.println("函数执行后的结果: " + result);
        // 两阶段都执行完毕后,再执行一个自定义函数
        assertTrue("Result was empty", result.length() > 0);
    }

    public static void main(String[] args) {
        runAfterBothExample();
    }
}

13. 使用BiConsumer处理两个阶段的结果

  • 使用BiConsumer处理两阶段请求
public class FutureTest {

    public static void assertEquals(String message, Object returnMessage){
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    static void thenAcceptBothExample() {
        String original = "Message";
        StringBuilder result = new StringBuilder();
        CompletableFuture.
                // 第一阶段执行
                completedFuture(original).
                    // 同步执行,将original转换为大写
                    thenApply(String::toUpperCase).
                        // 多阶段都执行
                        thenAcceptBoth(
                                CompletableFuture.
                                        // 第二阶段执行
                                        completedFuture(original).
                                            // 同步执行,将original转换为小写
                                            thenApply(
                                                    String::toLowerCase),(s1, s2) ->
                                                    // 将结果拼装到result
                                                    result.append(s1 + s2)
                        );
        assertEquals("MESSAGEmessage", result.toString());
    }

    public static void main(String[] args) {
        thenAcceptBothExample();
    }
}

14. 使用BiFunction处理两个阶段的结果

  • 假设CompletableFuture依赖前两个阶段结果,它复合两个阶段的结果再返回一个结果,这种情况就可以使用thenCombine()函数。
  • 此流程是同步的,getNow()会得到最终结果,它将大写和小写串联起来。
public class FutureTest {

    public static void assertEquals(String message, Object returnMessage){
        System.out.println("自定义值为:" + message);
        System.out.println("函数返回值为:" + returnMessage);
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static String delayedLowerCase(String s) {
        System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
        return s.toLowerCase();
    }

    static void thenCombineExample() {
        String original = "Message";
        CompletableFuture cf = CompletableFuture.completedFuture(original).thenApply(s -> delayedUpperCase(s))
                .thenCombine(CompletableFuture.completedFuture(original).thenApply(s -> delayedLowerCase(s)),
                        (s1, s2) -> s1 + s2);
        assertEquals("MESSAGEmessage", cf.getNow(null));
    }

    public static void main(String[] args) {
        thenCombineExample();
    }
}

15. 异步使用BiFunction处理两个阶段的结果

类似14,不同处为:

  • 依赖前两阶段,异步执行,thenCombine()也异步执行,尽管没有Async后缀。
public class FutureTest {

    public static void assertEquals(String message, Object returnMessage){
        System.out.println("自定义值为:" + message);
        System.out.println("函数返回值为:" + returnMessage);
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }

    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static String delayedLowerCase(String s) {
        System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
        return s.toLowerCase();
    }

    static void thenCombineExample() {
        String original = "Message";
        CompletableFuture cf = CompletableFuture.completedFuture(original)
                    // 异步执行
                    .thenApplyAsync(s -> delayedUpperCase(s))
                .thenCombine(CompletableFuture.completedFuture(original)
                                // 异步执行
                                .thenApplyAsync(s -> delayedLowerCase(s)),
                        (s1, s2) -> s1 + s2);
        assertEquals("MESSAGEmessage", cf.join());
    }

    public static void main(String[] args) {
        thenCombineExample();
    }
}

16. 组合 CompletableFuture

  • 使用thenCompose()完成14、15
  • 此方法等待一阶段完成(转换大写),将一阶段返回的结果,通过thenCompose放入自定义变量中,再执行二阶段函数(转换小写),并将一阶段结果变量与二阶段结果拼接。
public class FutureTest {

    public static void assertEquals(String message, Object returnMessage){
        System.out.println("自定义值为:" + message);
        System.out.println("函数返回值为:" + returnMessage);
        if(message.equals(returnMessage)) {
            System.out.println("响应信息一致");
        } else {
            System.out.println("响应信息不一致");
        }
    }
    
    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static String delayedLowerCase(String s) {
        System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
        return s.toLowerCase();
    }

    static void thenComposeExample() {
        String original = "Message";
        CompletableFuture cf =
                // 第一阶段,传入自定义original
                CompletableFuture.completedFuture(original).
                        // 同步执行,转化大写操作
                        thenApply(s -> delayedUpperCase(s))
                // 一阶段返回CompletableFuture(upper)向上转型了,并执行二阶段,传入自定义original,转换小写操作
                .thenCompose(upper -> CompletableFuture.completedFuture(original).
                        // 同步执行,转换小写操作
                        thenApply(s -> delayedLowerCase(s))
                // 一切执行完,将两次结果拼接
                .thenApply(s -> upper + s));
        // 判断响应结果是否一致
        assertEquals("MESSAGEmessage", cf.join());
    }

    public static void main(String[] args) {
        thenComposeExample();
    }
}

17. 完成任意一个阶段,创建一个阶段

  • 下面例子演示,任意一个CompletableFuture完成后,创建一个完成的CompletableFuture。
  • 示例说明
    • 待处理阶段首先创建,每个阶段都是转换大写操作。
    • 本阶段都是同步执行(thenApply),anyOf()创建的CompletableFuture会立即执行完毕。
    • 当所有阶段都完成时,使用whenComplete(BiConsumer<? super Object, ? super Throwable> action)处理完成结果。
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("全部转换为大写");
        } else {
            System.out.println("没有全部转换为大写");
        }
    }

    public static void assertTrue(String message, boolean done){
        if(done) {
            System.out.println(message + " ---- True 异步执行完成");
        } else {
            System.out.println(message + " ---- True 异步执行未完成");
        }
    }

    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static boolean isUpperCase(String s) {
        boolean flag = true;
        for(int i=0; i<s.length(); i++) {
            if (!Character.isUpperCase(s.charAt(i))) {
                flag = false;
            }
        }
        return flag;
    }

    static void anyOfExample() {
        StringBuilder result = new StringBuilder();
        // 定义一个List,包含3个小写字母元素
        List messages = Arrays.asList("a", "b", "c");
        // 迭代List,将所有值转换为大写
        List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
                .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase((String) s)))
                .collect(Collectors.toList());
        // 其中有一个转换了,就会马上创建一个完成的CompletableFuture,并提示,因为是同步的(thenApply),所以必定都已转换完毕
        CompletableFuture.anyOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((res, th) -> {
            // 迭代一阶段的结果,有一个转换成功则拼装结果,并执行最终结果
            if(th == null) {
                // 判断是否为大写
                assertTrue(isUpperCase((String) res));
                result.append(res);
            }
        });
        System.out.println("最终结果为: " + result);
        assertTrue("Result was empty", result.length() > 0);
    }

    public static void main(String[] args) {
        anyOfExample();
    }
}

18. 所有阶段全部完成,创建新阶段

  • 顾名思义,17是任意一个完成,创建新阶段,这个是必须全部完成,才允许创建新阶段
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("全部转换为大写");
        } else {
            System.out.println("没有全部转换为大写");
        }
    }

    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static boolean isUpperCase(String s) {
        boolean flag = true;
        for(int i=0; i<s.length(); i++) {
            if (!Character.isUpperCase(s.charAt(i))) {
                flag = false;
            }
        }
        return flag;
    }

    static void allOfExample() {
        StringBuilder result = new StringBuilder();
        List messages = Arrays.asList("a", "b", "c");
        List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
                .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase((String)s)))
                .collect(Collectors.toList());
        // 有一个执行完创建一个新阶段
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
            // 迭代执行情况,并且所有情况都创建新阶段
            futures.forEach(cf -> assertTrue(isUpperCase((String)cf.getNow(null))));
            result.append("done");
        });
    }

    public static void main(String[] args) {
        allOfExample();
    }
}

19. 所有阶段完成,异步创建新阶段

  • 使用thenApplyAsync()替换所有单个CompletableFutures函数
  • allOf()会在线程池中异步执行,所以需要调用join方法等待它完成
public class FutureTest {

    public static void assertTrue(boolean done){
        if(done) {
            System.out.println("全部转换为大写");
        } else {
            System.out.println("没有全部转换为大写");
        }
    }

    public static void assertTrue(String message, boolean done){
        if(done) {
            System.out.println(message + " ---- True 异步执行完成");
        } else {
            System.out.println(message + " ---- True 异步执行未完成");
        }
    }
    
    public static String delayedUpperCase(String s) {
        System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
        return s.toUpperCase();
    }

    public static boolean isUpperCase(String s) {
        boolean flag = true;
        for(int i=0; i<s.length(); i++) {
            if (!Character.isUpperCase(s.charAt(i))) {
                flag = false;
            }
        }
        return flag;
    }

    static void allOfAsyncExample() {
        StringBuilder result = new StringBuilder();
        List messages = Arrays.asList("a", "b", "c");
        List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
                // 异步执行第一阶段,使用thenApplyAsync
                .map(msg -> CompletableFuture.completedFuture(msg).thenApplyAsync(s -> delayedUpperCase((String) s)))
                .collect(Collectors.toList());
        // 当一阶段全部执行,分别创建所有新阶段
        CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
                .whenComplete((v, th) -> {
                    futures.forEach(cf -> assertTrue(isUpperCase((String) cf.getNow(null))));
                    result.append("done");
                });
        allOf.join();
        assertTrue("Result was empty", result.length() > 0);
    }

    public static void main(String[] args) {
        allOfAsyncExample();
    }
}

20. Action (实战)

  • 1 —— 19 介绍了一些CompletionStage 和 CompletableFuture部分函数。
  • 下面例子模拟真实场景,告诉你如何使用
    • 首先异步调用cars方法获取Car列表,获取CompletionStage场景。cars函数远程调用一个REST API
    • 然后为每个汽车添加评分,通过rating(manufacturerId)返回一个CompletionStage(阶段完成),它会异步获取汽车评分(可能又是一个新的Rest API)
    • 当列表内所有汽车评分填写完,调用allOf得到最终阶段,即前面阶段全部结束后执行
    • 最终阶段调用whenComplete()函数,打印每个汽车评分情况
// cars()返回一个汽车列表
cars().thenCompose(cars -> {
	// 迭代所有汽车实体
    List<CompletionStage> updatedCars = cars.stream()
    		// 为每个汽车打分
            .map(car -> rating(car.manufacturerId).thenApply(r -> {
                car.setRating(r);
                return car;
            })).collect(Collectors.toList());
  
  	// 当所有分数打完,迭代所有汽车
    CompletableFuture done = CompletableFuture
            .allOf(updatedCars.toArray(new CompletableFuture[updatedCars.size()]));
    // 返回所有汽车得分
    return done.thenApply(v -> updatedCars.stream().map(CompletionStage::toCompletableFuture)
            .map(CompletableFuture::join).collect(Collectors.toList()));
}).whenComplete((cars, th) -> {
    if (th == null) {
        cars.forEach(System.out::println);
    } else {
        throw new RuntimeException(th);
    }
}).toCompletableFuture().join();

每个汽车实体都是独立的,输出所有汽车评分可以异步执行,这会提高系统的性能(延迟),并且,等待所有的汽车评分被处理使用的是allOf方法,而不是手工的线程等待(Thread#join() 或 a CountDownLatch)。

已标记关键词 清除标记
课程简介: 历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页