java multithreading: application of Callable and FutureTask

Posted by hdpt00 on Fri, 11 Oct 2019 16:33:46 +0200

brief introduction

  • Callable

Callable is a class similar to Runnable. The main difference is that Callable can return results, but Runnable does not.

  • FutureTask

Simply put, FutureTask can be enabled, cancelled, and used with Callable and Runnable to determine whether the thread is complete. When used together with Callable, the return result can be obtained after the thread completes its task.

application

As we all know, why to use multithreading is to speed up the processing of tasks. For example, now you need to calculate the number of files in a directory, you can choose single-threaded traversal statistics, you can also consider using multi-threaded traversal queries.

public class FileCountCallableTest {

    public static void main(String[] args){
        //Fill in the folder directory you want to count
        File root = new File("D:\\");
        countByConcurrent(root);
        countBySingle(root);
    }

    /**
     * Multithread Computing File Number
     *
     * Get the subdirectories in the target directory and generate new threading tasks for each subdirectory to speed up the calculation.
     *
     * @param targetDir
     */
    static void countByConcurrent(File targetDir){
        try {
            long t1 = System.currentTimeMillis();
            File[] files = targetDir.listFiles();
            ExecutorService es = Executors.newFixedThreadPool(4);
            List<FutureTask> futureTaskList = new ArrayList<FutureTask>();
            int sumCount = 0;
            for(File file:files){
                if(file.isDirectory()){
                    //Generate new thread tasks per directory
                    FileCountCallable fileCountCallable = new FileCountCallable(file.getPath());
                    FutureTask<Integer> futureTask = new FutureTask(fileCountCallable);
                    es.submit(futureTask);
                    futureTaskList.add(futureTask);
                }else{
                    sumCount++;
                }
            }
            //Add up the results of each task to get the final result.
            for(FutureTask<Integer> futureTask:futureTaskList){
                sumCount += futureTask.get();
            }
            es.shutdown();
            System.out.println("sumCount:"+sumCount);
            System.out.println("countByConcurrent finish takes:"+(System.currentTimeMillis() - t1));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * Single thread counts the number of files
     *
     * @param targetDir
     */
    static void countBySingle(File targetDir){
        try {
            long t1 = System.currentTimeMillis();
            int sumCount = FileHelper.searchFiles(targetDir);
            System.out.println("sumCount:"+sumCount);
            System.out.println("countBySingle finish takes:"+(System.currentTimeMillis() - t1));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class FileCountCallable implements Callable<Integer>{

    private File root;

    public Integer call() {
        long t1 = System.currentTimeMillis();
        int count = FileHelper.searchFiles(root);
        return count;
    }

    public FileCountCallable(String pathName) {
        root = new File(pathName);
    }
}

Code is relatively simple, such as a directory with x, y, z three folders, I can traverse a thread to calculate, can also enable three threads to calculate separately, and finally add the results of the three threads is the final result.

Finally, I tested the results on this machine:

sumCount:155963
countByConcurrent finish takes:7793 ms
sumCount:155963
countBySingle finish takes:9608 ms

Using multi-threaded tasks saves 18.9% of the time, the difference is not very large, mainly because of the uneven distribution of my files. Sometimes the task workload is large, some are very small, and if the distribution is more uniform, the time can be saved up to 50%. Of course, we should adjust the multi-task strategy according to the actual needs, and try our best to make the workload of each task not very different.

There are also scenarios that can be used in our actual project:

  • http requests: for example, a function requires you to go to five APIs to get data.
  • File processing: such as image compression, watermarking
  • Database: multiple sql queries

Finally, I wish you all a lot of money as soon as possible.

Source address

Topics: Java Database SQL