Callable выполняется последовательно?

Всякий раз, когда я запускаю свою программу, реализующую функцию callable, я получаю вывод в последовательной форме.

Мол, вот моя программа:

package com.handson;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class WorkSheet_1 implements Callable<String> {

    /**
     * @param args
     */
    private int id;
    static int count = 0;
    public static String test[] = { "a1" , "a2" , "a3" , "a4" , "a5" , "a6" , "a7" , "a8" ,
                                    "b1" , "b2" , "b3" , "b4" , "b5" , "b6" , "b7" , "b8" ,
                                    "c1" , "c2" , "c3" , "c4" , "c5" , "c6" , "c7" , "c8" ,
                                    "d1" , "d2" , "d3" , "d4" , "d5" , "d6" , "d7" , "d8" ,
                                    "e1" , "e2" , "e3" , "e4" , "e5" , "e6" , "e7" , "e8" ,
                                    "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , "f8" ,
                                    "g1" , "g2" , "g3" , "g4" , "g5" , "g6" , "g7" , "g8" ,
                                    "h1" , "h2" , "h3" , "h4" , "h5" , "h6" , "h7" , "h8"}; 
    public WorkSheet_1(int id){
        this.id = id;
    }

    public static void main(String[] args) {
        try{
        // TODO Auto-generated method stub
            BlockingQueue blockingQueue = new ArrayBlockingQueue<WorkSheet_1>(48);
            ThreadPoolExecutor testExecutor = new ThreadPoolExecutor(6, 10, 1, TimeUnit.SECONDS, blockingQueue);
            for(int i = 0 ;i < test.length ;i++){
                Future<String> testFuture = testExecutor.submit(new WorkSheet_1(i));
                try {
                    System.out.println("Output Returned is : "+testFuture.get());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }catch(RejectedExecutionException e){
            e.printStackTrace();
        }
    }

    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        return "Called "+test[this.id];
    }



}

ВЫЗЫВАЕМЫЙ ВЫХОД:

Output Returned is : Called a1
Output Returned is : Called a2
Output Returned is : Called a3
Output Returned is : Called a4
Output Returned is : Called a5
Output Returned is : Called a6
Output Returned is : Called a7
Output Returned is : Called a8
Output Returned is : Called b1
Output Returned is : Called b2
Output Returned is : Called b3
Output Returned is : Called b4
Output Returned is : Called b5
Output Returned is : Called b6
Output Returned is ...............

Выходные данные всегда печатают массив последовательно, тогда как, когда я реализую работающий вывод, происходит в любом порядке:

ПУСКОВОЙ ВЫХОД:

Output Returned is : Called a1
Output Returned is : Called a3
Output Returned is : Called a7
Output Returned is : Called a8
Output Returned is : Called b1
Output Returned is : Called b2
Output Returned is : Called b3
Output Returned is : Called b4 ..............

Why such a difference ?

3 ответа

Решение

С помощью CompletionService нам не нужно печатать Sysout в функции вызова (как предусмотрено @Patricia) и есть преимущество использования CompletionService, чем Futures.

Для получения дополнительной информации обратитесь к http://www.baptiste-wicht.com/2010/09/java-concurrency-part-7-executors-and-thread-pools/. На сайте указано,

"Код, использующий будущее, немного сложен. И есть недостаток. Если для выполнения первой задачи требуется много времени, а все остальные задачи заканчиваются раньше первой, текущий поток не может вычислить результат до завершения первой задачи. Решение для этого Проблема в завершении службы "

Теперь решение с использованием сервиса Sompletion выдает желаемый результат:

    BlockingQueue<Runnable> blockingQueue   blockingQueue = new ArrayBlockingQueue<WorkSheet_1>(48);
    ThreadPoolExecutor testExecutor = new ThreadPoolExecutor(6, 16, 1,
            TimeUnit.SECONDS, blockingQueue, new CustomThreadFactory());

    CompletionService<String> completionService = new ExecutorCompletionService<String>(
            testExecutor);

    for (int i = 0; i < test.length; i++) {
        completionService.submit(new WorkSheet_1(i));
    }

    for (int i = 0; i < test.length; i++) {
        try {
            String result = completionService.take().get();
            System.out.println("Output Returned is : " + result);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // Compute the result
    }

Потому что вы ждете результата вызова в цикле for:

  System.out.println("Output Returned is : "+testFuture.get());

Метод Future.get() будет блокироваться, пока результат не станет доступен. Таким образом, вы отправляете следующий Callable только после того, как станет доступен предыдущий результат.

Чтобы увидеть чередование вывода, вам нужно сделать два изменения.

Первый, основанный на предыдущем ответе, состоит в том, чтобы переместить ожидание завершения из цикла, который ставит задачи в очередь. Это позволяет Callables работать параллельно, а не заставлять ждать возвращаемое значение после каждого. Возвращаемые значения по-прежнему отображаются в том порядке, в котором они были поставлены в очередь.

Второе изменение заключается в том, чтобы каждый Callable производил некоторый вывод во время работы. Если вы выводите данные только из основного потока после завершения, выходные данные должны быть в том порядке, в котором вы ожидаете завершения.

Поскольку Runnable не выдает возвращаемое значение, я уверен, что форма теста Runnable, по сути, сделала оба эти изменения. Они должны будут предоставлять свой собственный вывод во время работы, а не возвращать результат. Не было бы никакой причины ждать, пока каждый из них закончит.

Вот тестовая программа, основанная на оригинальной программе, с этими изменениями:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test implements Callable<String> {

  /**
   * @param args
   */
  private int id;
  static int count = 0;
  public static String test[] = { "a1", "a2", "a3", "a4", "a5", "a6", "a7",
      "a8",
      "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8",
      "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8",
      "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8",
      "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8",
      "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8",
      "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8",
      "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8" };

  public Test(int id) {
    this.id = id;
  }

  public static void main(String[] args) {
    try {
      // TODO Auto-generated method stub
      BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(
          48);
      ThreadPoolExecutor testExecutor = new ThreadPoolExecutor(6, 10, 1,
          TimeUnit.SECONDS, blockingQueue);
      List<Future<String>> futures = new ArrayList<>();
      for (int i = 0; i < test.length; i++) {
        Future<String> testFuture = testExecutor.submit(new Test(i));
        futures.add(testFuture);
      }
      for (Future<String> testFuture : futures) {
        try {
          System.out.println("Output Returned is : " + testFuture.get());
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } catch (ExecutionException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    } catch (RejectedExecutionException e) {
      e.printStackTrace();
    }
  }

  @Override
  public String call() throws Exception {
    System.out.println("Running " + test[this.id]);
    return "Called " + test[this.id];
  }

}

Образец вывода:

Running a1
Running a3
Running a2
Running a4
Running a5
Running a6
Running a7
Running b1
Running b5
Running a8
Running b7
Running b6
Running b2
Running b4
Running b3
Running c4
Running c3
Running c2
Running c1
Running b8
Running d1
Running c8
Running c7
Running c6
Output Returned is : Called a1
Output Returned is : Called a2
Output Returned is : Called a3
Running c5
Output Returned is : Called a4
Running d6
Running d5
Running d4
Running d3
Running d2
Running e3
Running e2
Running e1
Running d8
Output Returned is : Called a5
Running d7
Output Returned is : Called a6
Running e8
Running e7
Running e6
Running e5
Running e4
Running f5
Running f4
Running f3
Running f2
Output Returned is : Called a7
Running f1
Output Returned is : Called a8
Running g2
Running g1
Running f8
Running f7
Running f6
Running g7
Running g6
Running g5
Running g4
Output Returned is : Called b1
Running g3
Output Returned is : Called b2
Running h4
Running h3
Running h2
Running h1
Running g8
Running h8
Running h7
Running h6
Output Returned is : Called b3
Running h5
Output Returned is : Called b4
Output Returned is : Called b5
Output Returned is : Called b6
Output Returned is : Called b7
Output Returned is : Called b8
Output Returned is : Called c1
Output Returned is : Called c2
Output Returned is : Called c3
Output Returned is : Called c4
Output Returned is : Called c5
Output Returned is : Called c6
Output Returned is : Called c7
Output Returned is : Called c8
Output Returned is : Called d1
Output Returned is : Called d2
Output Returned is : Called d3
Output Returned is : Called d4
Output Returned is : Called d5
Output Returned is : Called d6
Output Returned is : Called d7
Output Returned is : Called d8
Output Returned is : Called e1
Output Returned is : Called e2
Output Returned is : Called e3
Output Returned is : Called e4
Output Returned is : Called e5
Output Returned is : Called e6
Output Returned is : Called e7
Output Returned is : Called e8
Output Returned is : Called f1
Output Returned is : Called f2
Output Returned is : Called f3
Output Returned is : Called f4
Output Returned is : Called f5
Output Returned is : Called f6
Output Returned is : Called f7
Output Returned is : Called f8
Output Returned is : Called g1
Output Returned is : Called g2
Output Returned is : Called g3
Output Returned is : Called g4
Output Returned is : Called g5
Output Returned is : Called g6
Output Returned is : Called g7
Output Returned is : Called g8
Output Returned is : Called h1
Output Returned is : Called h2
Output Returned is : Called h3
Output Returned is : Called h4
Output Returned is : Called h5
Output Returned is : Called h6
Output Returned is : Called h7
Output Returned is : Called h8

Сообщения "Running xx" начинаются более или менее по порядку, но быстро выходят из строя.

Другие вопросы по тегам