Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
In the previous article, we introduced a code optimization solution for request merging that can solve the database overload caused by a large number of requests, but let’s take a look at another solution.
In fact, when learning data structure and algorithm, we should be exposed to the idea of excessive and cure, in fact, it is a process of recursive call this function, in this process, constantly reduce the task, simplify the process of calculation. The same idea applies to system architecture. If a request accesses a large amount of data, we can split the task and execute it separately, and eventually return the results to the client.
Fork/Join, a multi-threaded execution framework provided in JDK 1.7, can split a large task into several smaller tasks, and finally summarize the results of each small task to obtain the results of a large task.
The ForkJoin framework provides us with RecursiveAction and RecursiveTask to create ForkJoin tasks. Simply put:
Recursiveaction
: used to create a task that has no return valueRecursiveTask
: used to create a task with a return value
For example, once again using our data from the previous section, we now have 1,000 items with ids from 0 to 999 in our database, and we want to sum their total value (don’t ask why we don’t just use the sum() function, just for example).
@ResponseBody
@RequestMapping("/single")
public int single(a) {
long startTime = System.currentTimeMillis();
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum += itemService.queryByCode(i + "").getPrice();
}
System.out.println(sum);
long endTime = System.currentTimeMillis();
System.out.println("Program runtime:" + (endTime - startTime) + "ms");
return sum;
}
Copy the code
Take a look at the running time, 5,235 milliseconds:
ForkJoin is used to divide tasks:
public class ForkJoinTask extends RecursiveTask<Integer> {
private int arr[];
private int start;
private int end;
private static final int MAX = 50;
public ForkJoinTask(int[] arr, int start, int end){
this.arr=arr;
this.start = start;
this.end = end;
}
@Override
protected Integer compute(a) {
int sum=0;
if((end - start) < MAX) {
// Do business directly
for (int i = start; i < end; i++) {
sum += arr[i];
}
return sum;
} else{
// Continue to split
int middle = (start + end) / 2;
ForkJoinTask left=new ForkJoinTask(arr, start, middle);
ForkJoinTask right=new ForkJoinTask(arr, middle, end);
left.fork();
right.fork();
returnleft.join() + right.join(); }}}Copy the code
Run the test again:
@ResponseBody
@RequestMapping("/fork")
public int forkJoin(a) {
long startTime = System.currentTimeMillis();
int arr[] = new int[1000];
for (int i = 0; i < 1000; i++) {
arr[i]=i;
}
ForkJoinPool pool=new ForkJoinPool();
ForkJoinTask task=new ForkJoinTask(arr,0,arr.length);
Integer sum = pool.invoke(task);
System.out.println(sum);
long endTime = System.currentTimeMillis();
System.out.println("Program runtime:" + (endTime - startTime) + "ms");
return sum;
}
Copy the code
Take a look at the program’s running time, which is only 6 milliseconds:
Do not feel a lot faster, directly will run speed up very much! Another reason why ForkJoin runs so fast is because a thread completes its own queue and then helps another thread to complete the task, which is also known as job stealing.
As shown in the figure above, after thread 1 has completed its task, it finds that thread 2 has unfinished tasks, and then it takes the unfinished tasks from thread 2 and puts the results back to thread 2.
In addition, we can speed things up further by increasing the number of threads, which can be configured and optimized for the business environment.
ForkJoinPool pool=new ForkJoinPool(Runtime.getRuntime().availableProcessors()*4);
Copy the code
Conclusion:
With the previous article, we introduced system optimization from the perspective of request merge and divide and conquer, and we can see that in ordinary work, code optimization has a long way to go. The code in this article can be obtained from my Github.
Github.com/trunks2008/…