java多线程8CallableFutureTask的应用

42次阅读

共计 2202 个字符,预计需要花费 6 分钟才能阅读完成。

简介

  • Callable

Callable 是类似 Runnable 的类,主要区别在于 Callable 是可以返回结果,而 Runnable 不会。

  • FutureTask

简单说 FutureTask 的作用是可以启用、取消、并且判断线程是否完成,可以搭配 Callable 和 Runnable 使用。和 Callable 一起使用时,可以实现在线程完成任务后获取返回结果。

应用

众所周知,为什么要使用多线程,无非是为了加快任务的处理速度。比如现在需要你计算某个目录下的文件数量,你可以选择单线程遍历统计,也可以考虑使用多线程遍历查询。

public class FileCountCallableTest {public static void main(String[] args){
        // 填写你想统计的文件夹目录
        File root = new File("D:\\");
        countByConcurrent(root);
        countBySingle(root);
    }

    /**
     * 多线程计算文件数量
     *
     * 获取目标目录下的子目录, 并把每个子目录生成新线程任务达到加快计算目的
     *
     * @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()){
                    // 每个目录生成新线程任务
                    FileCountCallable fileCountCallable = new FileCountCallable(file.getPath());
                    FutureTask<Integer> futureTask = new FutureTask(fileCountCallable);
                    es.submit(futureTask);
                    futureTaskList.add(futureTask);
                }else{sumCount++;}
            }
            // 把每个任务的计算结果相加,得出最终结果
            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();
        }
    }

    /**
     * 单线程计算文件数量
     *
     * @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);
    }
}

代码比较简单, 比如一个目录下有 x、y、z 三个文件夹,我可以一个线程去遍历计算,也可以启用 3 个线程分别去计算,最终把 3 个线程的结果相加就是最终结果。

最终我在本机上测试的结果:

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

采用多线程任务的方式,节省了 18.9% 的时间,相差并不是很大,主要是因为我的文件分布不均匀,采取以上策略时有的任务工作量大、有的很小,如果是分布比较均匀的话节省时间可以达到 50%。当然要根据实际的需求调整多任务的策略,尽量使每个任务的工作量相差并不大。

还有在我们实际项目中可以使用的场景:

  • http 请求:比如某个功能,需要你去 5 个 api 去获取数据。
  • 文件处理: 比如图片压缩、水印
  • 数据库:多个 sql 查询

最后祝大家早日發財

源码地址

正文完
 0