我可以在没有ExecutorService的情况下使用Callable线程吗?

时间:2014-08-10 17:20:56

标签: java multithreading concurrency executorservice java.util.concurrent

我可以在没有ExecutorService的情况下使用Callable线程吗?我们可以使用Runnable的实例和没有ExecutorService的Thread的子类,这个代码可以正常工作。但是这段代码始终如一:

public class Application2 {

    public static class WordLengthCallable implements Callable {
        public static int count = 0;
        private final int numberOfThread = count++;

        public Integer call() throws InterruptedException {
            int sum = 0;
            for (int i = 0; i < 100000; i++) {
               sum += i;
            }
            System.out.println(numberOfThread);
            return numberOfThread;
       }
   }
   public static void main(String[] args) throws InterruptedException {
       WordLengthCallable wordLengthCallable1 = new WordLengthCallable();
       WordLengthCallable wordLengthCallable2 = new WordLengthCallable();
       WordLengthCallable wordLengthCallable3 = new WordLengthCallable();
       WordLengthCallable wordLengthCallable4 = new WordLengthCallable();
       wordLengthCallable1.call();
       wordLengthCallable2.call();
       wordLengthCallable3.call();
       wordLengthCallable4.call();
       try {
           Thread.sleep(1000);
       } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.exit(0);
  }
}

使用ExecutorService,代码可以使用少量线程。我的错误在哪里?

4 个答案:

答案 0 :(得分:30)

虽然interface通常是根据预期的用例创建的,但它们绝不会被限制使用。

给定Runnable您可以将其提交给ExecutorService,或将其传递给Thread的构造函数,或者您可以直接调用其run()方法,就像您可以调用任何没有涉及多线程的interface方法。还有更多的用例,例如AWT EventQueue.invokeLater(Runnable)所以永远不要指望列表完整。

给定Callable,您有相同的选项,因此请务必强调,与您的问题不同,直接调用call()不涉及任何多线程。它只是像任何其他普通的方法调用一样执行方法。

由于没有构造函数Thread(Callable)使用CallableThread没有ExecutorService需要更多代码:

FutureTask<ResultType> futureTask = new FutureTask<>(callable);
Thread t=new Thread(futureTask);
t.start();
// …
ResultType result = futureTask.get(); // will wait for the async completion

答案 1 :(得分:6)

简单的直接答案是,如果要使用Callable创建和运行后台线程,则需要使用ExecutorService,当然,如果要获取Future对象或Futures集合,则需要使用ExecutorService。如果没有Future,您将无法轻松获取Callable返回的结果或轻松捕获生成的异常。当然你可以尝试将你的Callable包装在一个Runnable中,然后在一个Thread中运行它,但这会引起一个问题,因为这样做会导致你失去很多。


修改
你在评论中提问,

  

你的意思是下面的代码,哪个有效?

public class Application2 {
    public static class WordLengthCallable implements Callable {
    public static int count = 0;
    private final int numberOfThread = count++;

    public Integer call() throws InterruptedException {
        int sum = 0;
        for (int i = 0; i < 100000; i++) {
            sum += i;
        }
        System.out.println(numberOfThread);
        return numberOfThread;
    }
}
    public static void main(String[] args) throws InterruptedException {
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
        new Thread(new MyRunnable()).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    public static class MyRunnable implements Runnable {

        @Override
        public void run() {
            try {
                new WordLengthCallable().call();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我的回答:是的。链接中的代码&#34;排序&#34;作品。是的,它会创建后台线程,但会丢弃Callables中执行的计算结果,并忽略所有异常。这就是我的意思&#34;因为这样做会损失很多&#34;。


如,

  ExecutorService execService = Executors.newFixedThreadPool(THREAD_COUNT);
  List<Future<Integer>> futures = new ArrayList<>();
  for (int i = 0; i < THREAD_COUNT; i++) {
     futures.add(execService.submit(new WordLengthCallable()));
  }
  for (Future<Integer> future : futures) {
     try {
        System.out.println("Future result: " + future.get());
     } catch (ExecutionException e) {
        e.printStackTrace();
     }
  }

  Thread.sleep(1000);
  System.out.println("done!");
  execService.shutdown();

编辑2
或者,如果您希望在结果发生时返回结果,请使用CompletionService来包装您的ExecutorService,这是我以前从未尝试过的:

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CompletionServiceExample {
   public static class WordLengthCallable implements Callable<Integer> {
      private Random random = new Random();

      public Integer call() throws InterruptedException {
         int sleepTime = (2 + random.nextInt(16)) * 500;
         Thread.sleep(sleepTime);
         return sleepTime;
      }
   }

   private static final int THREAD_COUNT = 4;

   public static void main(String[] args) throws InterruptedException {
      ExecutorService execService = Executors.newFixedThreadPool(THREAD_COUNT);
      CompletionService<Integer> completionService = new ExecutorCompletionService<>(
            execService);

      for (int i = 0; i < THREAD_COUNT; i++) {
         completionService.submit(new WordLengthCallable());
      }
      execService.shutdown();

      try {
         while (!execService.isTerminated()) {
            int result = completionService.take().get().intValue();
            System.out.println("Result is: " + result);
         }
      } catch (ExecutionException e) {
         e.printStackTrace();
      }

      Thread.sleep(1000);
      System.out.println("done!");
   }
}

答案 2 :(得分:0)

是的,您可以直接使用自己的线程中的Callable的call()方法或Runnable的run()方法。但是,这应该是您在特殊情况下的最后手段(例如集成遗留代码或单元测试)。扫描程序可能会检测到此情况并提醒您可能存在架构问题,因此最好不要这样做。

您也可以使用自己的ExecutorService(或使用Guava&#39; s MoreExecutors.sameThreadExecutor()),它基本上调用调用线程中的调用。这将隔离你的'#cle;&#34;使用此Executor的接口,并允许它随时使用不同的Executor。

BTW:小心,当你从Thread继承时,你不应该在没有开始/停止的情况下使用它,因为这可能会导致泄漏。这是错误扫描程序直接调用run()方法时发出警报的原因之一。

答案 3 :(得分:0)

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MainClass {
    public static void main(String[] args) {
        try {
            Callable<String> c = () -> {
                System.out.println(Thread.currentThread().getName());
                return "true";
            };
            FutureTask<String> ft = new FutureTask<String>(c);
            Thread t = new Thread(ft);
            t.start();

            String result = ft.get();
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/*
   Output: 
   Thread-0 
   true
 */