image.PNG divide and conquer algorithm is a problem-solving strategy that decomposes a complex problem into several smaller, similar subproblems, solves these subproblems recursively, and then merges the solutions of these subproblems to obtain the solution of the original problem. The steps of a divide and conquer algorithm usually consist of three main parts:
Divide- Break down the original problem into a series of sub-questions. These sub-problems should be smaller and easier to solve versions of the original problem. Conquer- Recursively solve these sub-problems. If the size of the subproblems is small enough, then they can be solved directly;Otherwise, continue to break down the subproblem until it's small enough to be solved directly. Combine- Combine the solutions of sub-problems to form the solution of the original problem. The traditional thread pool ThreadPoolExecutor has two significant drawbacks:
Large tasks cannot be split, and a task can only be executed by a single thread;There is a race situation when a worker gets a task from the queue. In order to solve the shortcomings of the traditional thread pool, the fork join framework is introduced in J**A7. There are two parts, one is the forkjoinpool of the divide and conquer task, and the other is the forkjointask of the divide and conquer task. Stand-alone queues: In the fork join framework, each worker maintains its own double-ended queue (deque) to store tasks. When a thread creates a new subtask (byfork()
), these subtasks are placed in its own queue. Task scheduling: Each thread takes a task from its own queue to execute. Typically, threads take task executions from one end of the queue, which are recently added, following the LIFO (last-in, first-out) principle, which helps maintain the principle of locality and reduce context switching for task processing. Steal Missions: Job stealing is a task scheduling algorithm. In the fork join framework, when a worker thread has completed all the tasks in its own queue, while other threads have pending tasks in their queue, the thread can "steal" tasks from the other thread's queue to execute. Increase efficiencyThe advantage of this approach is that it minimizes the amount of time threads spend idle, thus improving overall processing efficiency. When one thread becomes idle, it helps other threads to complete their tasks so that no thread wastes CPU resources when there is nothing else to do. Dynamic load balancing: The work-stealing algorithm implements dynamic load balancing. Because tasks are dynamically redistributed at runtime, it is effective to handle those cases where individual subtasks are executed unevenly. Work TaskIn J**A's forkjoin framework, forkjointask is the most core abstract class, which represents a task that can be executed through the forkjoin framework. It mainly has the following characteristics:
forkjointaskExpandedfutureinterface, so it represents a task that may not have been completed but will be completed in the future. You can use the forkjointask method to check the status or get the result. ForkJoinTask is an abstract class with two main static subclasses: recursiveaction: for tasks that do not return resultsRecursiveTask: For tasks that need to return resultsThe key fork() and join() methods are defined in forkjointaskfork(): Asynchronous execution of a subtask, which is added to the queue of a worker thread in the forkjoin pool. join(): Wait for the result of the subtask, if the subtask has not been completed, it will block the wait. An abstract compute() method is also defined, which the subclasses need to implement as their main task. Internally, a thread-safe wait mechanism is used to coordinate threads waiting for joins. Find the maximum value in the largest array:
Pass an array in. Determine the threshold, if the length of the array is greater than 2, split the group. If it is less than or equal to 2, the comparison returns the maximum value.
private static class mytask extends recursivetask
override
A calculation method that compares elements in an array and returns a maximum value.
protected integer compute() else
Method entrance.
public static void main(string args) ;
forkjoinpool pool = new forkjoinpool();
mytask task = new mytask(arr);
Wait for the entire subtask to be executed.
system.out.println(pool.invoke(task));
The fork() method is used to asynchronously start a sub-forkjoin task and return immediately without any blocking. Subtasks started with fork() will be submitted to the worker queue in the current forkjoinpool to be executed. fork() will be executed in parallel with the main task (if CPU resources allow). Used to achieve"Partition"Parallel computation of split tasks. When using it, you need to match the join() method to wait for the subtask to be completed and obtain the calculation result. The join() method blocks the current thread until the target subtask is executed before returning a result (or throwing an exception). Join() achieves the blocking effect by waiting for the task state: uses a CAS-based loop to constantly check the task status, and if the task has not been completed, it will continue to wait, to achieve the blocking effect, and if the task is found to have completed or been canceled, the result or exception will be returned immediatelyThe internal waiting logic is thread-safe: by calling the internal method of forkjointaskinternalwaitdone()
to do thread-safe waiting, if the target task is completed, the result is returned immediately, and if it is not completed, it will be exploitedunsafe.park()
The method blocking the current thread is called when the task is completeunsafe.unpark()
to wake up a blocked thread supports a timeout mechanism: you can set a maximum wait time for join(), and a timeout exception will be returned if the threshold is exceeded. invoke = fork + joinFor the execution of a task, you must first execute the fork, block with join, invoke is equal to fork in the join first, join is equivalent to the future to determine whether there is a return value, if there is a direct return, if there is no park. If you don't call fork and call join directly, there will be a problem, it will always block, and the processing method will not be executed.