产生多个线程,从单个集合中获取输入并将结果放入单个集合中

时间:2014-01-19 18:28:03

标签: java multithreading

以下是我想要做的事情的简介,我有一个场景

  1. 每天动态生成多个文本文件。 0 每天8。每个文件的大小可以从小到大。取决于 一天的数据。
  2. 需要对它们进行一些检查(业务检查)。
  3. 我计划在最短的时间内完成任务,因此尝试编写并行执行程序来执行对这些文件的检查。

    我的想法是

    1. 将n个文件存储在并发集合(ConcurrentLinkedQueue)
    2. 删除文件,生成一个运行文件所有检查的线程
    3. 因为1个文件与另一个文件无关,我希望能够处理多个文件
    4. 将结果存储到另一个并发集合中(ConcurrentLinkedQueue ...转换为不同的html pdf报告)
    5. 注意:线程数可以与文件数不同(我想要配置的线程数,不是文件数=线程数的情况)
    6. 我的理解是这样我应该能够在最短的时间内完成每日检查。

      我的代码如下,让我感到困惑“如何在每个线程完成后将所有线程的结果存储在单个集合中”,我的直觉是我正在做一些有趣(不正确)的方式我正在存储结果。

      第二个问题想检查是否有人在下面的代码段中讨论任何其他问题

      第三个问题这似乎是一个常见的用例(对我来说)任何设计模式代码片段的指针解决这个问题

      注意:我使用的是JDK 6.

      public class CheckExecutor {
          // to store all results of all threads here , then this will be converted to html/pdf files
          static ConcurrentLinkedQueue<Result> fileWiseResult = new ConcurrentLinkedQueue<Result>();
      
          public static void main(String[] args) {
              int numberOfThreads=n; // need keep it configurable
              Collection<ABCCheck> checksToExecute // will populate from business logic , ABCCheck is interface , has a method check() , there are different implementations
      
              ConcurrentLinkedQueue<File> fileQueue = new ConcurrentLinkedQueue<File>(); // list of files for 1 day , may vary from 0 to 8
              int maxNumOfFiles = fileQueue.size();
      
              ThreadGroup tg = new ThreadGroup ("Group");
              // If more number of threads than files (rare , can be considered corener case)
              if (maxNumOfFiles < numberOfThreads) numberOfThreads=maxNumOfFiles;
              // loop and start number of threads
              for(int var=0;var<numberOfThreads;var++) 
              {
                  File currentFile = fileQueue.remove();
                  // execute all checks on 1 file using checksToExecute
                  ExecuteAllChecks checksToRun = new ExecuteAllChecks(); // business logic to populate checks
                  checksToRun.setchecksToExecute(checksToExecute);
                  checksToRun.setcheckResult(fileWiseResult); // when each check finishes want to store result here
                  new Thread (tg , checksToRun , "Threads for "+currentFile.getName()).start();
              }
      
              // To complete the tasak ... asap ... want to start a new thread as soon as any of current thread ends (diff files diff sizes)
              while(!fileQueue.isEmpty()) {
                  try {
                      Thread.sleep(10000); // Not sure If this will cause main thread to sleep (i think it will pause current thread ) i want to pause main thread
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  // check processing of how many files completed
                  if( (tg.activeCount()<numberOfThreads) && (fileQueue.size()>0) ) {
                      int numOfThreadsToStart = numberOfThreads - tg.activeCount();
                      for(int var1=0;var1<numOfThreadsToStart;var1++) {
                          File currentFile = fileQueue.remove();
                          ExecuteAllchecks checksToRun = new ExecuteAllchecks();
                          checksToRun.setchecksToExecute(checksToExecute);
                          checksToRun.setcheckResult(fileWiseResult); // when each check finishes want to store result here
                          new Thread (tg , checksToRun , "Threads for "+currentFile.getName()).start();
                      }
                  }
              }
          }
      }
      
      class ExecuteAllchecks implements Runnable {
      
          private Collection<ABCCheck> checksToExecute;
          private ConcurrentLinkedQueue<Result> checkResult; // not sure if its correct , i want to store result off all threads here
      
          public ConcurrentLinkedQueue<Result> getcheckResult() {
              return checkResult;
          }
      
          // plan to instantiate the result collection globally and store result here
          public void setcheckResult(ConcurrentLinkedQueue<Result> checkResult) {
              this.checkResult = checkResult;
          }
      
          public Collection<ABCCheck> getchecksToExecute() {
              return checksToExecute;
          }
      
          public void setchecksToExecute(Collection<ABCCheck> checksToExecute) {
              this.checksToExecute = checksToExecute;
          }
      
      
      
          @Override
          public void run() {
              Result currentFileResult = new Result();
              // TODO Auto-generated method stub
              System.out.println("Execute All checks for 1 file");
              // each check runs and calls setters on currentFileResult
              checkResult.add(currentFileResult);
          }
      
      }
      

1 个答案:

答案 0 :(得分:3)

实际的实现很大程度上受到计算本身的影响,但有些一般的方法可能是:

private final ExecutorService executor = Executors.newCachedThreadPool();
private final int taskCount = ...;
private void process() {
  Collection< Callable< Result > > tasks = new ArrayList<>( taskCount );
  for( int i = 0; i < taskCount; i++ ) {
      tasks.add( new Callable< Result >() {

        @Override
        public Result call() throws Exception {
            // TODO implement your logic and return result
            ...
            return result;
        }

      } );
  }
  List< Future< Result > > futures = executor.invokeAll( tasks );
  List< Result > results = new ArrayList<>( taskCount );
  for( Future< Result > future : futures ) {
      results.add( future.get() );
  }
}

我还建议在future.get()调用上使用合理的超时,以便执行线程不被卡住。

尽管如此,我还不建议在生产中使用缓存线程池,因为只要当前池没有足够的容量用于所有任务,此池就会增加,而是使用类似Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() )

的内容

我实际的任务可能是分成几个小的,后来加入考虑检查如何使用ForkJoin framework

有效地完成