CompletableFutur Multithreaded programming can be so smooth

When we do any development that involves asynchrony, when we design operations on shared variables, we tend to think of asynchronous multithreading, where one thread is slow to execute sequentially, and then multiple threads that execute together will be more efficient.

Multithreading operations on shared variables involve communication between threads, and visibility of shared variables between threads, which causes many multithreading problems.

In order to solve multithreading problems, for example, Java designed volatile to ensure visibility between threads and prevent instruction reordering. In order to ensure the operation of shared variables between threads, Java designed various synchronized locks, which also involves upgrading locks, biased locks, spin locks, heavyweight locks and so on. This gives us a daily focus on development and business, and we need to be cautious when using multiple threads.

If you’re dealing with multiple threads, the first thing that comes to mind is a thread pool, and you give the task to the thread pool. Is there a tool that encapsulates the thread pool as well? There’s a default thread pool.

In Java version 1.8, CompletableFuture is provided to support asynchronous programming. Let’s take a look at 🌰. In the project, I need to package the front end into various formats according to the List set. So would it be better to execute asynchronously, how many tasks (methods) and how many asynchronous threads are enabled to execute. The serial execution code is as follows:

GroupList = groupList(); map.put("groupSet", groupList); // List<Map<String, Object>> dateSheetList = dateSheet(groupList); dateSheetMap.put("dataSource", dateSheetList); map.put("dateSheet", dateSheetMap); List<Map<String, Object>> eventSheetList = eventSheet(); Map<String, Object> eventSheetMap = new HashMap<>(); eventSheetMap.put("dataSource", eventSheetList); map.put("eventSheet", eventSheetMap); List<Map<String, Object>> groupSheetList = groupSheet(eventSheetList); groupMap.put("dataSource", groupSheetList); map.put("groupSheet", groupMap);Copy the code

So for synchronous execution, the above four steps are performed sequentially by one thread, which is grouping, then assembling the date table, then assembling the event table, then assembling the grouping table. So I want these four steps to be executed separately by four threads, and then when these four threads are done executing each method, I’m going to return all the results back to the front end. CompletableFuture. The following article:

/ / grouping CompletableFuture < List < DisplayGroupsVo > > c1 = CompletableFuture. SupplyAsync (() - > {map. Put (" groupSet." groupList()); return groupList(); }); List<DisplayGroupsVo groupList = c1.get(); / / date form CompletableFuture < List < DisplayGroupsVo > > c2 = CompletableFuture. RunAsync (() - > {List < Map < String, Object>> dateSheetList = dateSheet(groupList); dateSheetMap.put("dataSource", dateSheetList); map.put("dateSheet", dateSheetMap); }); / / event form CompletableFuture < List < Map < String, Object > > > c3. = CompletableFuture supplyAsync (() - > {Map < String, Object> eventSheetMap = new HashMap<>(); eventSheetMap.put("dataSource", eventSheet()); map.put("eventSheet", eventSheetMap); return eventSheet() }); List<Map<String, Object>> eventSheetList = c3.get(); / / grouping form CompletableFuture < List < DisplayGroupsVo > > c4. = CompletableFuture runAsync (() - > {List < Map < String, Object>> groupSheetList = groupSheet(eventSheetList); groupMap.put("dataSource", groupSheetList); map.put("groupSheet", groupMap); }); CompletableFuture.allOf(c1, c2, c3, c4).join();Copy the code

The CompletableFuture is executed by multiple threads, and the four sequential steps are still executed by c1, C2, C3, and C4, After executing compleTableFuture.allof (c1, C2, C3, c4).join() means that all four threads are finished and the result is returned together. You’ll notice that with the CompletableFuture utility class you don’t need to worry about any multithreading.

  • There’s no manual maintenance of the thread, there’s no manual maintenance of the thread, there’s no task assignment thread to worry about.

  • The code is focused on the business and doesn’t need to focus on anything that’s multithreaded.

The front end needs to complete all the four tasks before returning them to the front end. If I perform the third task, I need to wait until the first and second tasks are completed before performing the third task. CompletableFuture also supports the following:

CompletableFuture<List<Map<String, Object>>> c3 =c1.thenCombine(c2,(other, fn)->{
    Map<String, Object> eventSheetMap = new HashMap<>();
    eventSheetMap.put("dataSource", eventSheet());
    map.put("eventSheet", eventSheetMap);
});
Copy the code
  • Task C3 waits until both task C1 and task c2 are completed, and the semantics are clear.

I used supplyAsync() and the runAsync() and allOf() methods provided by CompletableFuture above, so let’s see how these three methods execute asynchronously and we don’t need to worry about task multithreading.

  • RunAsync () method

  • If the runAsync() interface’s Run () method does not return a value, new threads are started to execute the task. The default thread pool is ForkJoinPool. The thread pool created by default the number of threads is the number of nuclear CPU can be through the JVM parameter Settings (JVM option:-Djava.util.concurrent.ForkJoinPool.com mon. Parallelism)

  • SupplyAsync () method

  • The Supplier interface of supplyAsync() has a return value from the get() method, which calls the GET () method.

Of course, there are times when more than one module needs to be called asynchronously. If we use the default ForkJoinPool, and if one of the tasks has a slow I/O, it will slow down the other modules, we want each module to have its own separate thread pool. For example, the event analysis module and the user behavior analysis module have separate thread pools, and CompletableFutur also provides methods that simply pass in their respective thread pools as arguments in calls to runAsync() and supplyAsync().

  • AllOf () method

Wait for multiple CompletableFutur tasks to return all the results, and then return all of them. Multiple tasks are the relation of and, and there is an anyOf() method, which is the relation of or, and only one of the tasks to return is needed to return the final result.

CompletableFuture makes asynchronous programming relatively easy, allowing you to quickly implement multithreaded asynchronous programming without even having to manually create a thread pool, making it easy to use during development. Go and try it!