Before jdk1.8, we used the get method of FutureTask to get the result of asynchronous execution.

Before the demo, post the shared code.

ConcurrentSupport:

public class ConcurrentSupport {
    public static String processOne() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getNow() + "#one";
    }

    public static String processTwo() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getNow() + "#two";
    }

    public static String processThree() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getNow() + "#three";
    }

    public static String getNow() {
        return LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
    }
}

TaskA:

class TaskA implements Callable {
    @Override
    public Object call() throws Exception {
        return ConcurrentSupport.processOne();
    }
}

TaskB:

class TaskB implements Callable {
    @Override
    public Object call() throws Exception {
        return ConcurrentSupport.processTwo();
    }
}

TaskC:

class TaskC implements Callable {
    @Override
    public Object call() throws Exception {
        return ConcurrentSupport.processThree();
    }
}

common case

For example, there are 3 tasks, which take 1s, 2s, and 3s respectively. When executed synchronously, the entire time is 6s.

public class NormalDemo {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        System.out.println(ConcurrentSupport.processOne());
        System.out.println(ConcurrentSupport.processTwo());
        System.out.println(ConcurrentSupport.processThree());
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

FutureTask

In the case of FutureTask, it is the one that takes the longest time, so the final time is 3s.

public class FutureTaskDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();
        // 创建FutureTask
        FutureTask taskA = new FutureTask(new TaskA());
        FutureTask taskB = new FutureTask(new TaskB());
        FutureTask taskC = new FutureTask(new TaskC());
        executor.submit(taskA);
        executor.submit(taskB);
        executor.submit(taskC);
        System.out.println(taskC.get());
        System.out.println(taskA.get());
        System.out.println(taskB.get());
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

CompletionService

But FutureTask also has a small flaw. For example, the above TaskC takes the longest execution time, directly blocking the printing tasks of TaskA and TaskB, and the printing results are three, one, two.

Is there any way to print (or otherwise process the result) which task is successfully executed first?

This is what CompletionService does. From the results, it can be seen that one, two, and three are printed.

public class CompletionServiceDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();
        // 创建CompletionService
        CompletionService<String> cs = new ExecutorCompletionService<>(executor);
        // 用于保存Future对象
        List<Future<String>> futures = new ArrayList<>(3);
        // 提交FutureTask
        futures.add(cs.submit(new TaskC()));
        futures.add(cs.submit(new TaskA()));
        futures.add(cs.submit(new TaskB()));
        for (int i = 0; i < 3; i++) {
            String result = cs.take().get();
            System.out.println(result);
        }
        System.out.println("耗时:" + (System.currentTimeMillis() - start));
    }
}

CompletableFuture

If there is a task like this, A1 executes A2, B1 executes B2, A2 and B2 execute, and then executes C.

JDK1.8 provides CompletableFuture this elegant solution.

For example, in the following example, f3 is executed after f1 and f2 are executed.

In the method of CompletableFuture, runAsync has no return value, and supplyAsync has a return value.

public class CompletableFutureDemo {
    public static void main(String[] args) {
        CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
                    String one = ConcurrentSupport.processOne();
                    System.out.println(one);
                }
        );
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
                    String two = ConcurrentSupport.processTwo();
                    System.out.println(two);
                    return two;
                }
        );
        CompletableFuture<String> f3 =
                f1.thenCombine(f2, (__, tf) -> {
                            System.out.println("f3#" + tf);
                            return "f3";
                        }
                );
        f3.join();
    }
}

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆