CompletableFuture: make your code suffer from blockages

Improve application performance when it is easy to think of asynchronous, asynchronous to handle a number of tasks so that the main thread can respond as quickly as possible.

EDITORIAL

By reading this article you will learn:

  • CompletableFuture use
  • CompletableFure asynchronous and synchronous performance test
  • Future Why have CompletableFuture still need to be introduced in JDK1.8
  • CompletableFuture application scenarios
  • Optimization of the use of CompletableFuture

Scene Description

Discover all the shops and return to a commodity price, a store and query the price of commodities API for synchronizing a Shop class that provides a synchronization method is called getPrice

  • Shop category: Shop.java
public class Shop {
    private Random random = new Random();
    /**
     * 根据产品名查找价格
     * */
    public double getPrice(String product) {
        return calculatePrice(product);
    }

    /**
     * 计算价格
     *
     * @param product
     * @return
     * */
    private double calculatePrice(String product) {
        delay();
        //random.nextDouble()随机返回折扣
        return random.nextDouble() * product.charAt(0) + product.charAt(1);
    }

    /**
     * 通过睡眠模拟其他耗时操作
     * */
    private void delay() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码

Query commodity price synchronization method, and other operations by sleep analog method. This scenario simulates when you need to call a third-party API, but the API provided by third parties are synchronized, when you can not modify the code to call the third-party API how to design performance and improve application throughput, which can be used when CompletableFuture class

CompletableFuture use

Completable Future implementation class interface is introduced in JDK1.8

  • CompletableFuture creation:

    • Using new methods

      CompletableFuture<Double> futurePrice = new CompletableFuture<>();
      复制代码
    • CompletableFuture # completedFuture created using the static method

      public static <U> CompletableFuture<U> completedFuture(U value) {
          return new CompletableFuture<U>((value == null) ? NIL : value);
      }
      复制代码

      Task execution result parameter is finished, the general method is seldom used in practical applications

    • There are two overloaded methods to create supplyAsync use CompletableFuture # supplyAsync static methods:

      //方法一
      public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
          return asyncSupplyStage(asyncPool, supplier);
      }
      //方法二
      public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                         Executor executor) {
          return asyncSupplyStage(screenExecutor(executor), supplier);
      }
      复制代码
    • There are two ways to create runAsync overloaded static method using CompletableFuture # runAsync

      //方法一
      public static CompletableFuture<Void> runAsync(Runnable runnable) {
          return asyncRunStage(asyncPool, runnable);
      }
      //方法二
      public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) {
          return asyncRunStage(screenExecutor(executor), runnable);
      }
      复制代码

    Description:

    • The difference between the two overloads => which may be passed to customize the Executor, the former is the default, use ForkJoinPool
    • The difference between the methods supplyAsync and runAsync => The former has a return value, which is no return value
    • Supplier is a function interface, this method requires incoming class that implements this interface, tracking the source code will find the method in the run method calls the interface. Therefore CompletableFuture create objects using this method simply override the get method Supplier of defined tasks to get in the process. And because the function interface can use Lambda expressions, and create new objects compared CompletableFuture code concise lot
  • The results of the acquisition: to get CompltableFuture class results provide four ways

    //方式一
    public T get()
    //方式二
    public T get(long timeout, TimeUnit unit)
    //方式三
    public T getNow(T valueIfAbsent)
    //方式四
    public T join()
    复制代码

    Description:

    • get () and get (long timeout, TimeUnit unit) => In the Future has been provided, which provides time-out processing, if the result is not acquired within the specified time will throw an exception timeout
    • getNow => Now get results is not blocked, the abnormal result of the calculation has been completed or return results of calculation, is not calculated if the completion set value to be returned valueIfAbsent
    • join => method does not throw in

    Example:

    public class AcquireResultTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //getNow方法测试
            CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(60 * 1000 * 60 );
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                return "hello world";
            });
    
            System.out.println(cp1.getNow("hello h2t"));
    
            //join方法测试
            CompletableFuture<Integer> cp2 = CompletableFuture.supplyAsync((()-> 1 / 0));
            System.out.println(cp2.join());
    
            //get方法测试
            CompletableFuture<Integer> cp3 = CompletableFuture.supplyAsync((()-> 1 / 0));
            System.out.println(cp3.get());
        }
    }
    复制代码

    Description:

    • The first execution result is hello h2t, because you want to sleep for one minute can not get immediate results
    • join to get the result in the method will not throw an exception, but the results will throw an exception, an exception is thrown CompletionException
    • get to get the result in the method will throw an exception, the results of the thrown exception is ExecutionException
  • Exception Handling: to create a static method of CompletableFuture objects without displaying the handling exceptions, objects created using the new method needs to be called completeExceptionally set to capture anomalies, example:

    CompletableFuture completableFuture = new CompletableFuture();
    new Thread(() -> {
         try {
             //doSomething,调用complete方法将其他方法的执行结果记录在completableFuture对象中
             completableFuture.complete(null);
         } catch (Exception e) {
             //异常处理
             completableFuture.completeExceptionally(e);
          }
     }).start();
    复制代码

Pick synchronization method asynchronous method to query all the shops some commodity prices

Shop is a list:

private static List<Shop> shopList = Arrays.asList(
        new Shop("BestPrice"),
        new Shop("LetsSaveBig"),
        new Shop("MyFavoriteShop"),
        new Shop("BuyItAll")
);
复制代码

Synchronization method:

private static List<String> findPriceSync(String product) {
    return shopList.stream()
            .map(shop -> String.format("%s price is %.2f",
                    shop.getName(), shop.getPrice(product)))  //格式转换
            .collect(Collectors.toList());
}
复制代码

Asynchronous methods:

private static List<String> findPriceAsync(String product) {
    List<CompletableFuture<String>> completableFutureList = shopList.stream()
            //转异步执行
            .map(shop -> CompletableFuture.supplyAsync(
                    () -> String.format("%s price is %.2f",
                            shop.getName(), shop.getPrice(product))))  //格式转换
            .collect(Collectors.toList());

    return completableFutureList.stream()
            .map(CompletableFuture::join)  //获取结果不会抛出异常
            .collect(Collectors.toList());
}
复制代码

Performance test results:

Find Price Sync Done in 4141
Find Price Async Done in 1033
复制代码

Asynchronous execution efficiency four-fold increase

Why still need CompletableFuture

In JDK1.8 before, so that tasks can submit method calls the thread pool to run asynchronously, which returns a Future object, get the results asynchronous execution by calling the get method:

private static List<String> findPriceFutureAsync(String product) {
    ExecutorService es = Executors.newCachedThreadPool();
    List<Future<String>> futureList = shopList.stream().map(shop -> es.submit(() -> String.format("%s price is %.2f",
            shop.getName(), shop.getPrice(product)))).collect(Collectors.toList());

    return futureList.stream()
            .map(f -> {
                String result = null;
                try {
                    result = f.get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                return result;
            }).collect(Collectors.toList());
}
复制代码

Both Sheng Yu Sheng-liang, why still need to introduce CompletableFuture?
For simple business scenarios using the Future did not, but want to calculate the results of multiple asynchronous tasks combined, after a calculation asynchronous tasks required before a value, and so on asynchronous tasks, use that point API Future provided on the bag shy, elegant enough to deal with, this time to let CompletableFuture declarative way to deal with these demands elegance

Other API Introduction

whenComplete calculation process:

The results of the foregoing processing, not return the new value
provides three methods:

//方法一
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
//方法二
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
//方法三
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
复制代码

Description:

  • BiFunction <? Super T ,? super U ,? extends V> fn = parameter> defining processing of the results
  • Executor executor parameter => custom thread pool
  • Will perform a combination of operations to the end of the async method in a new thread

Example:

public class WhenCompleteTest {
    public static void main(String[] args) {
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "hello");
        CompletableFuture<String> cf2 = cf1.whenComplete((v, e) ->
                System.out.println(String.format("value:%s, exception:%s", v, e)));
        System.out.println(cf2.join());
    }
}
复制代码

thenApply conversion:

The calculation result is transmitted to the front CompletableFuture thenApply, thenApply return results after processing. It can be considered accomplished by thenApply method CompletableFuture<T>to CompletableFuture<U>conversion. Vernacular point is the calculation result as a parameter CompletableFuture thenApply method returns the result of processing thenApply method
provides three methods:

//方法一
public <U> CompletableFuture<U> thenApply(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(null, fn);
}

//方法二
public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(asyncPool, fn);
}

//方法三
public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn, Executor executor) {
    return uniApplyStage(screenExecutor(executor), fn);
}
复制代码

Description:

  • Function <? Super T ,? extends U> fn = parameter> before a conversion operation on the calculation result CompletableFuture
  • Executor executor parameter => custom thread pool
  • Combining operation will be performed in the example a new thread to the end of the async method:
public class ThenApplyTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenApplyTest::randomInteger).thenApply((i) -> i * 8);
        System.out.println(result.get());
    }

    public static Integer randomInteger() {
        return 10;
    }
}
复制代码

Here the calculated result of the expansion of eight times before a CompletableFuture

thenAccept result of the process:

thenApply can also be classified as processing of the results, the difference between thenAccept and thenApply is no return value
provides three methods:

//方法一
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
    return uniAcceptStage(null, action);
}

//方法二
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
    return uniAcceptStage(asyncPool, action);
}

//方法三
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,
                                               Executor executor) {
    return uniAcceptStage(screenExecutor(executor), action);
}
复制代码

Description:

  • Consumer <? Super T> action parameter => operation on the previous calculation result CompletableFuture
  • Executor executor parameter => custom thread pool
  • Similarly exemplary combination of operations will be executed in a new thread to the end of the async method:
public class ThenAcceptTest {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(ThenAcceptTest::getList).thenAccept(strList -> strList.stream()
                .forEach(m -> System.out.println(m)));
    }

    public static List<String> getList() {
        return Arrays.asList("a", "b", "c");
    }
}
复制代码

The calculated results are printed out before a CompletableFuture

thenCompose asynchronous pipelining results:

thenCompose two methods may be asynchronous operation pipelining
provides three methods:

//方法一
public <U> CompletableFuture<U> thenCompose(
    Function<? super T, ? extends CompletionStage<U>> fn) {
    return uniComposeStage(null, fn);
}

//方法二
public <U> CompletableFuture<U> thenComposeAsync(
    Function<? super T, ? extends CompletionStage<U>> fn) {
    return uniComposeStage(asyncPool, fn);
}

//方法三
public <U> CompletableFuture<U> thenComposeAsync(
    Function<? super T, ? extends CompletionStage<U>> fn,
    Executor executor) {
    return uniComposeStage(screenExecutor(executor), fn);
}
复制代码

Description:

  • Function<? super T, ? extends CompletionStage<U>> fnExecution parameter => the current calculation result CompletableFuture
  • Executor executor parameter => custom thread pool
  • The same operation will be performed in combination of a new thread to the end of the async method of
    example:
public class ThenComposeTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenComposeTest::getInteger)
                .thenCompose(i -> CompletableFuture.supplyAsync(() -> i * 10));
        System.out.println(result.get());
    }

    private static int getInteger() {
        return 666;
    }

    private static int expandValue(int num) {
        return num * 10;
    }
}
复制代码

Execution Flow:

thenCombine combination of results:

The two independent methods thenCombine CompletableFuture combined, does not depend on the results of the second Completable first Completable to
provide three methods:

//方法一
public <U,V> CompletableFuture<V> thenCombine( 
    CompletionStage<? extends U> other,
    BiFunction<? super T,? super U,? extends V> fn) {
    return biApplyStage(null, other, fn);
}
  //方法二
  public <U,V> CompletableFuture<V> thenCombineAsync(
      CompletionStage<? extends U> other,
      BiFunction<? super T,? super U,? extends V> fn) {
      return biApplyStage(asyncPool, other, fn);
  }

  //方法三
  public <U,V> CompletableFuture<V> thenCombineAsync(
      CompletionStage<? extends U> other,
      BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
      return biApplyStage(screenExecutor(executor), other, fn);
  }
复制代码

Description:

  • CompletionStage <? Extends U> other parameters => new calculation of CompletableFuture
  • BiFunction <? Super T ,? super U ,? extends V> fn = parameter> defines two objects CompletableFuture After completion of the calculation of how to combine the results, this parameter is a function interface, it is possible to use Lambda expressions
  • Executor executor parameter => custom thread pool
  • The same combination will perform the operation to the end of the async method in a new thread

Example:

public class ThenCombineTest {
    private static Random random = new Random();
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Integer> result = CompletableFuture.supplyAsync(ThenCombineTest::randomInteger).thenCombine(
                CompletableFuture.supplyAsync(ThenCombineTest::randomInteger), (i, j) -> i * j
        );

        System.out.println(result.get());
    }

    public static Integer randomInteger() {
        return random.nextInt(100);
    }
}
复制代码

The two threads make a calculated value of the multiplication performed in the flowchart returns:

allOf & anyOf combining a plurality CompletableFuture:

Methods Introduction:

//allOf
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    return andTree(cfs, 0, cfs.length - 1);
}
//anyOf
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
    return orTree(cfs, 0, cfs.length - 1);
}
复制代码

Description:

  • allOf => All CompletableFuture perform after performing calculations.
  • anyOf => any executes a calculation is performed after CompletableFuture

Example:

  • allOf test method
    public class AllOfTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Void> future1 = CompletableFuture.supplyAsync(() -> {
                System.out.println("hello");
                return null;
            });
            CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> {
                System.out.println("world"); return null;
            });
            CompletableFuture<Void> result = CompletableFuture.allOf(future1, future2);
            System.out.println(result.get());
        }
    }
    复制代码
    allOf method does not return value, return value and there is no need for all the previous task is finished scenarios to perform follow-up tasks
  • anyOf test method
    public class AnyOfTest {
        private static Random random = new Random();
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
                randomSleep();
                System.out.println("hello");
                return "hello";});
            CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
                randomSleep();
                System.out.println("world");
                return "world";
            });
            CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
            System.out.println(result.get());
       }
    
        private static void randomSleep() {
            try {
                Thread.sleep(random.nextInt(10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    复制代码
    The two threads will print out the results, but the get method only return results from the first to complete the task. This approach is good as long as there is a return value can continue to perform other tasks application scenarios

important point

Many methods are available to achieve asynchronous async suffix [with], but these need to be careful to use asynchronous methods, because there is asynchronous means that context switching, synchronization may not necessarily better than the performance. If you need to use asynchronous methods do first test , speak with test data! ! !

CompletableFuture application scenarios

There are IO-intensive tasks can be selected CompletableFuture, IO part handed over to another thread to execute. Logback, implementation principle Log4j2 asynchronous logging is played a new thread to perform the IO operation, this part can be CompletableFuture.runAsync (() -> {ioOperation ();}) way to invoke the relevant Logback asynchronous logging principle can refer to this article Logback asynchronous logging . If it is not recommended to use a CPU-intensive recommend using parallel streams

Optimization of space

supplyAsync underlying implementation tasks:

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}
static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>();
    e.execute(new AsyncSupply<U>(d, f));
    return d;
}
复制代码

Low-level calls that thread pool to perform the task, and CompletableFuture the default thread pool is ForkJoinPool

private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
复制代码

ForkJoinPool thread pool size depends on the number of CPU cores. Before writing Why Alibaba To disable the Executors create a thread pool? The article mentioned, CPU-intensive task thread pool size configured CPU cores on it, but the size of the IO-intensive, thread pool by ** CPU Number of CPU utilization * * (1 + thread waiting time / thread CPU time) ** OK. The CompletableFuture application scenarios is IO-intensive tasks, so the default ForkJoinPool generally unable to achieve the best performance, we need to create your own thread pool based on business

Last Annex: sample code , welcome Fork and Star

Past articles attached: you are welcome to read, thumbs up, comment

Concurrent related
1. Why Alibaba To disable the Executors create a thread pool?
2. do their own thing, exception handling thread

Design Patterns Related:
1. singleton, you really write on it?
2. (+ factory model strategy mode + map) packages Kill project switch case

JAVA8 Related:
1. Use the Stream API optimized code
2. pro, it is recommended that you use instead of Date oh LocalDateTime

Database-related:
query efficiency 1. mysql database time type datetime, bigint, timestamp comparison
2.'m glad! Finally stepped on a slow pit query

Efficient Related:
1 line and a Java scaffolding, unified project team structure style

Log Related:
1. Log frame, select Logback Or Log4j2?
2. Logback profile so written, TPS 10 times

Project related:
1. nothing else, hands-on writing a local cache LRU
2. Redis achieve thumbs functional modules
3. JMX visual monitoring thread pool
4. Rights Management [SpringSecurity articles]
5. Spring custom annotation from entry to the master
6 . java simulated landing Youku
7. QPS so high, it would be to write a multi-level cache bar
8. java using phantomjs screenshot

Other:
1. Use the try-with-resources elegance close the resource
2. boss, why should the amount of storage with a float deduct my wages

Guess you like

Origin juejin.im/post/5dfb5bc951882512420b06c5