JUC high concurrency programming

Posted by Zhadus on Sun, 05 Dec 2021 17:12:08 +0100

12. Fork/Join branch merge framework

1. Introduction

  1. 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.
    
  2. 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 namedescribe
      RecursiveActionFor tasks that do not return results
      RecursiveTaskUsed 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());
        }
    }
    
    

Topics: Java Concurrent Programming