Optimizing business code with FutureTask

brief introduction

  • FutureTask: a cancelable asynchronous calculation.
  • FutureTask provides the basic implementation of Future. You can use FutureTask to execute tasks asynchronously and call its get method to get the returned results.

Objectives of this chapter

  • Demonstrates the code of paging query users in general.
  • Use FutureTask to optimize the code of paging query users to improve efficiency.

Paging query user cases

Create UserService class

  • Generally, paging query data is divided into the following two parts.
    • (1) Total number of query data.
    • (2) Query data in pages. sql uses offset and limit parameters.
  • The following is divided into two methods (total number and paging data). In order to simulate the effect of querying data, it takes 2 milliseconds. The code is shown below.
    /***
     * Number of query users
     * */
    private int userCount() {
        try {
            //The simulation here takes 2 milliseconds to query the database
            Thread.sleep(2L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 10;
    }
    /***
     * Query user specific data according to the number of pages
     * @param currentPage the number of pages
     * */
    private List userData(int currentPage) {
        try {
            //The simulation here takes 2 milliseconds to query the database
            Thread.sleep(2L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }
  • Call these two methods to calculate the query time, and the code is as follows.
    public Object findUserPageList() {
        long startTime = System.currentTimeMillis();
        //1. Number of query users
        int userCount = userCount();
        System.out.println(userCount);
        //2. Paging query of user data
        List list = userData(1);
        System.out.println(list);
        long endTime = System.currentTimeMillis();
        // Time difference, time-consuming
        long diffTime = endTime - startTime;
        System.out.println("Total time:" + diffTime);
        return null;
    }

Create UserController class

  • Declare that the controller layer calls the service layer method, and the code is as follows.
@RestController
public class UserController {
    @Resource
    private UserService userService;
    @RequestMapping("/findUserPageList")
    public Object findUserPageList(){
        return userService.findUserPageList();
    }
}

Query effect

  • Call the / findUserPageList interface 3 times, and the effect is shown in the figure below.

Optimizing business code with FutureTask

  • As can be seen from the above figure, the longest is 6 milliseconds. So how to optimize it?
  • The answer is asynchronous, but ordinary threads can't because they can't receive the return value.
  • FutureTask is provided in the JUC package for developers to use.
  • Let's start the journey of FutureTask. Just change the UserService#findUserPageList method.

Modify UserService code

  • Create a FutureTask object, pass in a Callable object in the constructor, use the sub thread to execute, and finally call the get method to get the return value. The code is shown below.
  public Object findUserPageList() {
        long startTime = System.currentTimeMillis();
        //1. Number of query users
        // userCount();
        Callable userCountFutureCallable = new Callable() {
            @Override
            public Integer call() throws Exception {
                return userCount();
            }
        };
        FutureTask<Integer> userCountFuture = new FutureTask(userCountFutureCallable);
        //2. Paging query of user data
        // userData(1);
        Callable userDataFutureCallable = new Callable() {
            @Override
            public List call() throws Exception {
                return userData(1);
            }
        };
        FutureTask<List> userDataFuture = new FutureTask(userDataFutureCallable);
        // Execution using child threads
        new Thread(userCountFuture).start();
        new Thread(userDataFuture).start();

        Integer userCount = 0;
        try {
            userCount = userCountFuture.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(userCount);
        List list = null;
        try {
            list = userDataFuture.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(list);
        long endTime = System.currentTimeMillis();
        // Time difference, time-consuming
        long diffTime = endTime - startTime;
        System.out.println("Total time:" + diffTime+"millisecond");
        return null;
    }

Modified effect

  • The / findUserPageList interface is still called 3 times, and the results are shown in the figure below.
  • As can be seen from the above figure, the efficiency has been greatly improved.

Give FutureTask to thread pool for execution

  • In fact, there are still areas to be optimized. For example, it is now to create a sub thread every call. As we all know, it is impossible to create sub threads indefinitely, and there is a great loss in the creation and destruction of threads. You can use thread pool at this time. The code is as follows.
    // Number of CPU cores
    private final int processors = Runtime.getRuntime().availableProcessors();
    private final ExecutorService executorService = new ThreadPoolExecutor(processors * 2, processors * 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue(processors * 100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
   public Object findUserPageList() {
        long startTime = System.currentTimeMillis();
        //1. Number of query users
        // userCount();
        Callable userCountFutureCallable = new Callable() {
            @Override
            public Integer call() throws Exception {
                return userCount();
            }
        };
        FutureTask<Integer> userCountFuture = new FutureTask(userCountFutureCallable);
        //2. Paging query of user data
        // userData(1);
        Callable userDataFutureCallable = new Callable() {
            @Override
            public List call() throws Exception {
                return userData(1);
            }
        };
        FutureTask<List> userDataFuture = new FutureTask(userDataFutureCallable);
        // Execution using child threads
        /*new Thread(userCountFuture).start();
        new Thread(userDataFuture).start();
*/
        executorService.execute(userCountFuture);
        executorService.execute(userDataFuture);

        Integer userCount = 0;
        try {
            userCount = userCountFuture.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(userCount);
        List list = null;
        try {
            list = userDataFuture.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(list);
        long endTime = System.currentTimeMillis();
        // Time difference, time-consuming
        long diffTime = endTime - startTime;
        System.out.println("Total time:" + diffTime+"millisecond");
        return null;
    }

Tags: Java Programming Multithreading

Posted by Vebut on Tue, 03 May 2022 02:51:09 +0300