Java CompletableFuture 详解

Java CompletableFuture 详解

参考1:异步技巧之CompletableFuture
参考2:通过实例理解 JDK8 的 CompletableFuture
参考3:CompletableFuture 详解
参考4:多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>JavaExample</groupId>
	<artifactId>JavaExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>

		<!-- https://mvnrepository.com/artifact/junit/junit -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>23.0</version>
		</dependency>

	</dependencies>

	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.7.0</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
1、JDK1.8之前的Future接口
1.1、 定义:Future用来代表异步的结果,并且提供了检查计算完成,等待完成,检索结果完成等方法,需要完成后再进行回调。
1.2、缺陷
1.2.1、将两个异步计算合成一个异步计算,这两个异步计算互相独立,同时第二个又依赖第一个的结果。
1.2.2、当Future集合中某个任务最快结束时,返回结果。
1.2.3、等待Future结合中的所有任务都完成。
1.2.4、通过编程方式完成一个Future任务的执行。
1.2.5、应对Future的完成时间。也就是我们的回调通知。
2、JDK1.8的CompletableFuture 接口
2.1、实现了CompletionStage和Future接口,扩展了Future,简化异步编程复杂性,提供了函数式编程能力,可通过回调方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
3、实践示例
总结:Runnable类型的参数会忽略计算的结果
Consumer是纯消费计算结果,BiConsumer会组合另外一个CompletionStage纯消费
Function会对计算结果做转换,BiFunction会组合另外一个CompletionStage的计算结果做转换。
package net.liuzd.java.completable.future;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.junit.Test;

public class TestCompletableFuture {

    private static Random rand = new Random();

    void println(Object val) {
        System.out.println(val);
    }

    @Test
    public void testCompletedFuture() {
        // completedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture
        CompletableFuture<String> cf = CompletableFuture.completedFuture("message");
        assertTrue(cf.isDone());
        // getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值
        String val = cf.getNow("没返回值或者异常,在这指定返回值!");
        println("1>" + val);
        assertEquals("message", val);
    }

    @Test
    public void testJoin() {
        //
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            int i = 1 / 0;
            return rand.nextInt();
        });
        // future.get();强制处理异常(throws InterruptedException, ExecutionException)
        Integer val = future.join();
        println("2>" + val);
    }

    public static CompletableFuture<Integer> compute() {
        final CompletableFuture<Integer> future = new CompletableFuture<>();
        // 没有关联任何的Callback、线程池、异步任务等,如果客户端调用future.get就会一致傻等下去
        return future;
    }

    @Test
    public void testComplete() {
        // 声明一个异步
        CompletableFuture<Integer> f = compute();
        // implements Runnable
        class Client extends Thread {

            CompletableFuture<Integer> cf;

            Client(String threadName, CompletableFuture<Integer> _cf) {
                super(threadName);
                cf = _cf;
            }

            @Override
            public void run() {
                try {
                    println(getName() + ": " + cf.get());
                } catch (InterruptedException | ExecutionException e) {
                    println("没有获取到正确的结果,在这发送邮件通知相关人员..." + e.getMessage());
                }
            }
        }
        //
        new Client("Client1", f).start();
        new Client("Client2", f).start();
        println("waiting ...");
        // CompletableFuture.complete()、CompletableFuture.completeExceptionally只能被调用一次
        // 触发完成操作,这是get的结果
        f.complete(rand.nextInt());
        // 可以抛出一个异常,而不是一个成功的计算结果:
        // f.completeExceptionally(new Exception("不想给你结果,怎么样??"));
        // 使用場景在哪 ??
        // 思考下:通过一系列计算,在这给定一个返回结果,让上面二个线程得到结果后去做其它事情 ??
        println("3>");
    }

    static int getMoreData() {
        try {
            // int i = 2 / 0;
            Thread.sleep(100);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return rand.nextInt(1000);
    }

    @Test
    public void testWhenCompleteAsync() throws InterruptedException, ExecutionException {
        // 方法以runAsync表示无返回值,supplyAsync表示有返回值
        // 没有指定Executor的方法会使用ForkJoinPool.commonPool()作为它的线程池执行异步代码
        // 计算结果完成时的处理
        // 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(TestCompletableFuture::getMoreData);
        Future<Integer> f = future.whenCompleteAsync((v, e) -> {
            // 执行异步任务,得到结果后,再去其它事情
            println("异步返回结果:" + v);
            println("异常:" + e);
        });
        println("4>" + f.get());
    }

    @Test
    public void testThenAccept() throws InterruptedException, ExecutionException {
        // 纯消费(执行Action)
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 100;
        });
        // 返回值为NULL>参数类型是函数式接口Consumer,只有输入,没有返回值。
        CompletableFuture<Void> f = future.thenAccept(v -> {
            // 对结果进行消费,不做其它处理
            System.out.println("总算等到你的结果了 : " + v);
        });
        println("5>" + f.get());
    }

    @Test
    public void testThenAcceptBothAsync() throws InterruptedException, ExecutionException {
        // thenAcceptBoth以及相关方法提供了类似的功能,当两个CompletionStage都正常完成计算的时候,就会执行提供的action
        // 它用来组合另外一个异步的结果。
        CompletableFuture<Integer> xFuture = CompletableFuture.supplyAsync(() -> {
            return 100;
        });
        CompletableFuture<Integer> yFuture = CompletableFuture.supplyAsync(() -> {
            return 100;
        });
        // 返回值为NULL>参数类型是函数式接口Consumer,只有输入,没有返回值。
        xFuture.thenAcceptBothAsync(yFuture, (x, y) -> {
            System.out.println("对二个结果处理:" + x * y);
        });
        // runAfterBoth是当两个CompletionStage都正常完成计算的时候,执行一个Runnable,这个Runnable并不使用计算的结果
        xFuture.runAfterBoth(yFuture, () -> {
            System.out.println("到我了...,你们的东西我没兴趣要!");
        });
        println("6>");
    }

    @Test
    public void testThenRun() throws InterruptedException, ExecutionException {
        // 当计算完成的时候会执行一个Runnable,与thenAccept不同,Runnable并不使用CompletableFuture计算的结果
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 100;
        });
        CompletableFuture<Void> f = future.thenRun(() -> {
            System.out.println("你的东西,我没兴趣要,总算完成了,该我上场了...");
        });
        println("7>" + f.get());
    }

    @Test
    public void testThenCompose() throws InterruptedException, ExecutionException {
        // 组合新值返回
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 18;
        });
        CompletableFuture<String> f = future.thenCompose(age -> {
            return CompletableFuture.supplyAsync(() -> {
                return String.format("张三的年龄%d", age);
            });
        });
        println("8>" + f.get());
    }

    @Test
    public void testThenCombine() throws InterruptedException, ExecutionException {
        // 功能更类似thenAcceptBoth,只不过thenAcceptBoth是纯消费,它的函数参数没有返回值,而thenCombine的函数参数fn有返回值
        CompletableFuture<Integer> age = CompletableFuture.supplyAsync(() -> {
            return 18;
        });
        CompletableFuture<String> name = CompletableFuture.supplyAsync(() -> {
            return "张三的年龄";
        });
        CompletableFuture<String> result = age.thenCombine(name, (x, y) -> String.format(y + "%s", x));
        println("9>" + result.get());
    }

    @Test
    public void testAcceptEither() throws InterruptedException, ExecutionException {
        // acceptEither方法是当任意一个CompletionStage完成的时候,action这个消费者就会被执行返回Void
        CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(10000 + rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "张三";
        });
        CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
            return "李四";
        });
        CompletableFuture<Void> result = name1.acceptEither(name2, name -> {
            println("谁第一个报名:" + name.toString());
        });
        println("10>" + result.get());
    }

    @Test
    public void testApplyToEither() throws InterruptedException, ExecutionException {
        // acceptEither当任意一个CompletionStage完成的时候,fn会被执行,它的返回值会当作新的CompletableFuture<U>的计算结果
        CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(10000 + rand.nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "张三";
        });
        CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
            return "李四";
        });
        CompletableFuture<String> result = name1.applyToEither(name2, name -> {
            println("到底谁第一个报名?" + name);
            return name;
        });
        println("11>第一个报名是:" + result.get());
    }

    @Test
    public void testAllof() throws InterruptedException, ExecutionException {
        // allOf方法是当所有的CompletableFuture都执行完后执行计算
        CompletableFuture<String> name = CompletableFuture.supplyAsync(() -> {
            String queryName = "张三";
            println("查询完成:姓名>" + queryName);
            return queryName;
        });
        CompletableFuture<Integer> age = CompletableFuture.supplyAsync(() -> {
            int queryAge = 18;
            println("查询完成:年龄>" + queryAge);
            return queryAge;
        });
        CompletableFuture<String> sex = CompletableFuture.supplyAsync(() -> {
            String querySex = "男";
            println("查询完成:性别>" + querySex);
            return querySex;
        });
        CompletableFuture<Void> f = CompletableFuture.allOf(name, age, sex);
        println("12>" + f.get());
    }

    @Test
    public void testAnyOf() throws InterruptedException, ExecutionException {
        // anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同
        CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "张三";
        });
        CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "李四";
        });
        CompletableFuture<String> name3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(rand.nextInt(100));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "王五";
        });
        // applyToEither只是判断两个CompletableFuture,anyOf返回值的计算结果是参数中其中一个CompletableFuture的计算结果
        // applyToEither返回值的计算结果却是要经过fn处理的。当然还有静态方法的区别,线程池的选择等
        CompletableFuture<Object> result = CompletableFuture.anyOf(name1, name2, name3);
        println("13>第一个报名是:" + result.get());
    }

    private static ExecutorService exs = Executors.newFixedThreadPool(10);

    private List<Node> getNodes(Integer parentId, String name) {
        List<Node> nodes = new ArrayList<>();
        int size = rand.nextInt(10);
        for (int i = 0; i < size; i++) {
            nodes.add(new Node(rand.nextInt(), parentId, name + rand.nextInt()));
        }
        return nodes;
    }

    @SuppressWarnings("rawtypes")
    @Test
    public void testAll() throws InterruptedException, ExecutionException {
        // 综合运用>加载子集
        // 查询父级(从缓存中获取)
        CompletableFuture<List<Node>> future = CompletableFuture.supplyAsync(() -> {
            return getNodes(null, "parent-");
        });
        Future<List<Node>> result = future.whenCompleteAsync((nodes, error) -> {
            // 执行异步任务,得到结果后,加载子对象
            CompletableFuture[] cfs = nodes.stream().map(node -> CompletableFuture.supplyAsync(() -> loadChild(node),
                    exs).whenComplete((v, childError) -> {
                        println("已完成加载子集:" + v);
                    })).toArray(CompletableFuture[]::new);

            CompletableFuture.allOf(cfs).join();
        });
        //
        List<Node> nodes = result.get();
        nodes.stream().forEach(node -> {
            println("result:" + node);
        });
        //
    }

    private Node loadChild(Node parent) {
        parent.adds(getNodes(parent.id, "child-"));
        return parent;
    }

    class Node {

        Integer    id;
        Integer    parentId;
        String     name;
        List<Node> childs;

        Node(Integer _id, Integer _parentId, String _name) {
            id = _id;
            parentId = _parentId;
            name = _name;
            childs = new ArrayList<>();
        }

        void adds(List<Node> _childs) {
            childs.addAll(_childs);
        }

        @Override
        public String toString() {
            return "Node [id=" + id + ", parentId=" + parentId + ", name=" + name + ", childs=" + childs + "]";
        }
    }

}

转载于:https://my.oschina.net/liuzidong/blog/3062195

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值