12. Fork/Join branch merge framework
1. Introduction
-
Fork/Join can split a large task into multiple subtasks for parallel processing, and finally merge the subtask results into the final calculation results for output. The Fork/Join framework does two things
Fork: Split a complex task and make it small Join: Merge the split task calculation results 1,Task segmentation: first Fork/Join The framework needs to divide large tasks into sufficiently small tasks. If the subtasks are relatively large, the subtasks should be further divided 2,Execute the task and merge the results: put the split admission into the dual end queue, and then start several threads to obtain the task execution from the dual end queue. The results of subtask execution will be put into another queue. Start a thread to get the results from the queue, and then merge the data.
-
use
-
ForkJoinTask: to use the Fork/Join framework, we first need to create a Fork/Join task. This class provides a mechanism to re execute the fork and Join in the task. Generally, we do not need to directly inherit the ForkJoinTask class, but only its subclasses. The Fork/Join framework provides two subclasses
Class name describe RecursiveAction For tasks that do not return results RecursiveTask Used for tasks that have returned results ForkJoinPool: ForkJoinTask needs to be executed through ForkJoinPool
Recursive task: tasks that can be called recursively (by themselves) after inheritance.
-
Fork/Join class diagram
-
2. Code implementation
package com.codetip.codejuc.juc.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RecursiveTask; class MyTask extends RecursiveTask<Integer> { // The check score difference cannot exceed yes, and is calculated within 10 private static final Integer value = 10; // Start value of split private int begin; // End value of split private int end; // Return results private int result; // Create parametric construction method MyTask(int bengin, int end) { this.begin = bengin; this.end = end; } // Process of splitting and merging @Override protected Integer compute() { // Judge whether the start and end values are greater than 10, if ((end - begin) <= value) { for (int i = begin; i <= end; i++) { result = result + i; } } else { // Get intermediate value int middle = (begin + end) / 2; // Split left MyTask task01 = new MyTask(begin, middle); // Split right MyTask task02 = new MyTask(middle + 1, end); // Split complete task01.fork(); task02.fork(); // Consolidated results result = task01.join() + task02.join(); } return result; } } // ForkJoin code demo public class ForkJoinDemo01 { public static void main(String[] args) throws ExecutionException, InterruptedException { // Create MyTask object MyTask task = new MyTask(0, 101); // Create Split Merge pool object ForkJoinPool forkJoinPool = new ForkJoinPool(); // Submit task ForkJoinTask<Integer> submit = forkJoinPool.submit(task); // Get final merge results Integer result = submit.get(); System.out.println(result); } }
13. Completable futrue asynchronous callback
1. Introduction
CompletableFuture stay Java It is used for asynchronous programming. Asynchronous usually means non blocking, which can make our task run separately from the main thread. Through callback, we can get the execution status, completion, exception and other information of asynchronous task in the main thread. CompletableFuture Realized Future, CompletionStage Interface, implemented Future The interface can be compatible with the current wired process pool framework, and CompletionStage Interface is the interface abstraction of asynchronous programming, which defines a variety of asynchronous methods. Through the collection of these two methods, a powerful interface is created CompletableFuture class
2. Completable Future and Future
Futrue in Java is usually used to represent the reference of an asynchronous task. For example, we submit the task to the thread pool, and then we will get a futrue. In Future, there is an isDone method to judge whether the task is processed or not. There is also a get method that can block until the task is finished and then obtain the results. However, this method is synchronous as a whole, Because the client needs to keep blocking and waiting or polling to know whether the task is completed
The main disadvantages of Future are as follows
-
Manual completion is not supported
I submitted a task, but the execution is too slow. I have obtained the task result through other paths. Now I can't notify the executing thread of the task result, so I must actively cancel or wait for it to complete
-
Further non blocking calls are not supported
adopt Future of get Method will block until the task is completed, but you want to perform additional tasks after getting the task because Future Callback function is not supported, so this function cannot be implemented
-
Chained calls are not supported
about Future We want to move on to the next one Future Processing is used to form a chain pipline Call, this is Future It can't be achieved in
-
Multiple Future merges are not supported
For example, we have 10 Future Parallel execution, we want in all Future After running, it is impossible to execute some functions Future Realized
-
Exception handling is not supported
Future of API There is no exception handling apiļ¼Therefore, in asynchronous operation, if there is a problem, it is not easy to locate
3. Using completablefuture
-
Scenario: create a completable future in the main thread, and then the main thread will block when calling the get method. Finally, we will terminate it in a child thread.
Code case
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class CompleteTableFutureDemo01 { public static void main(String[] args) throws ExecutionException, InterruptedException { /* Create a completable future in the main thread, and then the main thread will block when calling the get method. Finally, we will terminate it in a child thread*/ CompletableFuture<String> completableFuture = new CompletableFuture<>(); new Thread(() -> { try { System.out.println(Thread.currentThread().getName() + " The child thread starts working!!!"); //The child thread sleeps for 5 seconds TimeUnit.SECONDS.sleep(5); //Complete the main thread in the child thread completableFuture.complete("hello CompletableFuture"); } catch (Exception e) { e.printStackTrace(); } }, "A").start(); //Main thread calling get method blocking //System.out.println(completableFuture.get()); System.out.println("Main thread complete,End of blocking!!!!!!"); } }
-
Asynchronous task with no return value
- Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class CompleteTableFutureDemo02 { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("The main thread starts executing!!!!!!!!!!!!"); CompletableFuture<Void> demo2 = CompletableFuture.runAsync(() -> { try { System.out.println("The child thread starts working"); TimeUnit.SECONDS.sleep(2); System.out.println("Child thread work completed"); } catch (Exception e) { e.printStackTrace(); } }); //Main thread blocking demo2.get(); // Calling the get method blocks the main thread from executing // The main thread continues to execute the task System.out.println("The main thread continues to execute the task"); TimeUnit.SECONDS.sleep(5); System.out.println("Main thread end"); } }
-
Asynchronous task with return value
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class CompleteTableFutureDemo03 { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("Main thread task start"); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { try { System.out.println("Sub thread execution task starts!!!"); TimeUnit.SECONDS.sleep(2); System.out.println("The execution task of sub thread ends!!!"); } catch (InterruptedException e) { e.printStackTrace(); } return "888"; }); // The main thread is blocked, waiting for the result of the child thread String result = completableFuture.get(); // Calling the get method blocks the main thread from executing System.out.println("Main thread end,The child thread results are:" + result); } }
-
Thread dependency
When one thread depends on another thread, you can use thenApply Method to serialize the two threads
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo04 { private static Integer num = 10; // When a thread depends on another thread, the thenApply method can be used to serialize the two threads public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); return num += 10; }).thenApply(u -> u * u); // Schedule another thread to execute the task /*.thenApply(u->{ return u * u; }); */ // Blocking the main thread waiting for results Integer result = completableFuture.get(); // Calling the get method blocks the main thread from executing System.out.println("The execution of the main thread ends, and the result of the sub thread is:" + result); } }
-
Consumption processing results
thenAccept Consumption processing results, Receive the processing result of the task and consume it. No result is returned
Code demonstration:
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo05 { private static Integer num = 10; // When a thread depends on another thread, the thenApply method can be used to serialize the two threads public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); return num += 10; }).thenApply(u -> { System.out.println("Square start"); return u * u; }).thenAccept(v -> System.out.println("All child threads are processed,Finally called. accept,The result is:" + v)); } }
-
exception handling
exceptionally exception handling,Triggered when an exception occurs
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo06 { private static Integer num = 10; //Exception handling, triggered when an exception occurs public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); int i = 1 / 0; return num += 10; }).exceptionally((ex) -> { if (ex != null) { System.out.println(ex.getMessage()); return -1; } else { return num; } }); } }
-
handle
handle be similar to thenAccept/thenRun method,This is the last processing call,But you can handle exceptions at the same time
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo07 { private static Integer num = 10; // Handle is similar to thenAccept/thenRun method. It is the last processing call, but it can handle exceptions at the same time public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { int i = 1 / 0;// Simulation anomaly System.out.println("Add 10 to start the task"); return num += 10; }).handle((v, ex) -> { // If an exception occurs, return - 1 if (ex != null) { return -1; } else { // If there is no abnormality, add 5 to the result return v + 5; } }); // Main thread blocking get results System.out.println(completableFuture.get()); } }
-
Result merging
-
thenCompose Merge two dependent CompletableFutures Implementation results of
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo08 { private static int num = 10; public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); return num += 10; }); //merge CompletableFuture<Integer> completableFuture1 = completableFuture.thenCompose(i -> CompletableFuture.supplyAsync(() -> i + 3)); System.out.println(completableFuture.get()); System.out.println(completableFuture1.get()); } }
-
thenCombine Merge two independent CompletableFutures task
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.BiFunction; import java.util.function.Function; public class CompleteTableFutureDemo09 { private static int num = 10; // thenCombine merges two completable futures tasks that have no dependencies public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); return num += 10; }); CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> { System.out.println("Multiply by 10 to start the task"); return num * 10; }); CompletableFuture<Object> completableFuture3 = completableFuture1.thenCombine(completableFuture2, (a, b) -> { List<Integer> list = new ArrayList<>(); list.add(a); list.add(b); return list; }); System.out.println("The consolidated results are: "+completableFuture3.get()); } }
-
Merge the results of multiple tasks allOf and anyOf
allOf: A series of independent future Task, and do something after all the tasks are completed
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; public class CompleteTableFutureDemo10 { private static int num = 10; public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("Main thread start"); List<CompletableFuture> list = new ArrayList<>(); CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> { System.out.println("Add 10 to start the task"); num += 10; return num; }); list.add(job1); CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> { System.out.println("Multiply by 10 to start the task"); num = num * 10; return num; }); list.add(job2); CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> { System.out.println("Start with 10 tasks"); num = num - 10; return num; }); list.add(job3); CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> { System.out.println("Divided by 10, the task starts"); num = num / 10; return num; }); list.add(job4); //Multitask merge List<Integer> collect = list.stream().map(CompletableFuture<Integer>::join).collect(Collectors.toList()); System.out.println(collect); } }
anyOf: Just in multiple future There is a return, and the whole task can be ended without waiting for each one future end
Code demonstration
package com.codetip.codejuc.juc.completabkefutrue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; public class CompleteTableFutureDemo11 { private static int num = 10; public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("Main thread start"); CompletableFuture<Integer>[] futures = new CompletableFuture[4]; CompletableFuture<Integer> job1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(5000); System.out.println("Add 10 to start the task"); num += 10; return num; } catch (Exception e) { return 0; } }); futures[0] = job1; CompletableFuture<Integer> job2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); System.out.println("Multiply by 10 to start the task"); num = num * 10; return num; } catch (Exception e) { return 1; } }); futures[1] = job2; CompletableFuture<Integer> job3 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(3000); System.out.println("Start with 10 tasks"); num = num - 10; return num; } catch (Exception e) { return 2; } }); futures[2] = job3; CompletableFuture<Integer> job4 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(4000); System.out.println("Divided by 10, the task starts"); num = num / 10; return num; } catch (Exception e) { return 3; } }); futures[3] = job4; CompletableFuture<Object> future = CompletableFuture.anyOf(futures); System.out.println(future.get()); } }