这是使用执行程序服务实现多线程的正确方法吗?

时间:2019-02-06 11:34:52

标签: java multithreading

我正在尝试通过执行程序服务来教自己使用多线程,并且想知道在下面实现我的代码的最佳实践是什么-我正在读取文本文件目录并检查字符/单词-

如果我所做的只是通过文件列表对每个线程进行操作,我也很困惑是否正在使用多个线程。是否一次处理多个文件?

主班

public class Application {

    private long totalCharacterCount;
    private long totalLineCount;
    private final File[] fileList;
    private final static String _DIRECTORY = "src//documents";

    public Application(String directory){
        fileList = new File(directory).listFiles();
    }
    public synchronized File[] getFileList(){
        return fileList;
    }
    public static void main(String[] args) throws InterruptedException, ExecutionException {
           ExecutorService executor = Executors.newFixedThreadPool(4);

          Application x = new Application(_DIRECTORY);

          for(File file : x.getFileList()){
               Future<FileReadings> response = executor.submit(new Process(file));
               x.totalCharacterCount += response.get().characterCount;
               x.totalLineCount += response.get().lineCount;

          }
          System.out.println("Total lines in all documents: " + x.totalLineCount);
          System.out.println("Total characters in all documents: " + x.totalCharacterCount);
          executor.shutdown();
    }
}

进程类

public class Process implements Callable<FileReadings> {

    private FileReadings object;
    private File file;
    public Process(File file){
        FileReadings obj = new FileReadings();
        this.object = obj;

        this.file = file;
    }

    public void CountCharacters(File file){
        int count = 0;
        try {

            BufferedReader reader = Files.newBufferedReader(file.toPath());
            while(reader.read() != -1){
                count++;
            }
            object.characterCount = reader.read();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
            object.characterCount = count;
    }
    public void CountLines(File file){
        try {
            Stream<String> text = Files.lines(file.toPath());
            object.lineCount = text.count();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public FileReadings call() throws Exception {
        CountCharacters(this.file);
        CountLines(this.file);
        System.out.println("THERE WERE: " + object.characterCount + " CHARACTERS IN: " + file.getName());
        System.out.println("THERE WERE: " + object.lineCount + " LINES IN: " + file.getName());
        return object;
    }
}

文件阅读类

public class FileReadings {
     long characterCount;
     long lineCount;
}

结果:

THERE WERE: 65 CHARACTERS IN: test1.txt
THERE WERE: 10 LINES IN: test1.txt
THERE WERE: 88 CHARACTERS IN: test2.txt
THERE WERE: 21 LINES IN: test2.txt
Total lines in all documents: 31
Total characters in all documents: 153

1 个答案:

答案 0 :(得分:1)

不。这不是正确的方法。您提交一个进程,然后在将来调用get(),则阻塞并等待其完成,因此实际上这是一个同步处理。 有两种方法可以并行执行异步处理:

1)invokeAll()

这是一种更简单的方法,但是它要求您预先创建所有流程实例,因此它取决于要执行的并行任务数(如果有数百万个并行任务,则可能会达到内存限制)。一旦创建了流程,就可以立即将其提交给执行者。它将并行执行所有任务(根据线程池大小),并在所有任务完成后返回。

      List<Callable<FileReadings>> tasks = new Arraylist<>();
      for (File file : x.getFileList()) {
           tasks.add(new Process(file));
      }
      // submit all processes at once. they will be processed in parallel 
      // this call blocks until all tasks are finished
      List<Future<FileReadings>> responses = executor.invokeAll(tasks);
      // at this point all processes finished. all get() will return immediately
      for (Future<FileReadings> response : responses) {
           x.totalCharacterCount += response.get().characterCount;
           x.totalLineCount += response.get().lineCount;
      }

2)submit()

此解决方案在您创建进程并立即提交时具有更高的可伸缩性,因此内存需求是恒定的(不包括执行程序)。但是,您需要自己管理响应:

      List<Future<FileReadings>> responses = new ArrayList<>();
      for (File file : x.getFileList()) {
           responses.add(executor.submit(new Process(file)));
      }
      // at this point all processes submitted but not finished.
      // need to check which is finished at intervarls
      while (responses.isEmpty() == false) {
          Thread.sleep(1000);  // allow some processing time for tasks
          // ListIterator allows removing items
          ListIterator<Future<FileReadings>> itr = responses.listIterator();
          while (itr.hasNext()) {
               Future<FileReadings> response = itr.next();
               // if task is complete, get it and remove from list
               if (response.isDone()) {
                   x.totalCharacterCount += response.get().characterCount;
                   x.totalLineCount += response.get().lineCount;
                   itr.remove();
               }
          }
      }