代码之家  ›  专栏  ›  技术社区  ›  Prateek

可调用是否按顺序执行?

  •  3
  • Prateek  · 技术社区  · 11 年前

    每当我运行实现可调用的程序时,我都会得到顺序形式的输出。

    比如,这是我的节目:

    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 回复  |  直到 11 年前
        1
  •  5
  •   René Link    11 年前

    因为您在for循环中等待可调用的结果:

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

    Future.get()方法将被阻塞,直到结果可用为止。因此,只有在上一个结果可用后,您才能提交下一个Callable。

        2
  •  4
  •   Patricia Shanahan    11 年前

    要查看输出的交错,您需要进行两次更改。

    第一个,基于前面的答案,是将等待完成的任务从队列中移出。这样做允许Callables并行运行,而不是在每个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”消息开始时或多或少按顺序排列,但很快就会出错。

        3
  •  2
  •   Prateek    11 年前

    使用 CompletionService 我们不需要打印 Sysout 在调用函数中(由@Patricia提供),使用CompletionService比Futures有好处。

    有关更多信息,请参阅 java-concurrency-executors-and-thread-pools . 该网站表示,

    “使用future的代码有点复杂。而且有一个缺点。如果 第一个任务和所有其他任务需要很长时间来计算 在第一个之前结束,当前线程无法计算结果 在第一个任务结束之前。此问题的解决方案是“完成服务”

    现在,使用Sompletion Service的解决方案打印所需结果:

        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
        }