Can I use Callable threads without ExecutorService?

后端 未结 4 1827
花落未央
花落未央 2021-02-02 11:00

Can I use Callable threads without ExecutorService? We can use instances of Runnable and subclasses of Thread without ExecutorService and this code works normally. But this code

相关标签:
4条回答
  • 2021-02-02 11:41
    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
     */
    
    0 讨论(0)
  • 2021-02-02 11:43

    While interfaces are often created with an intended use case, they are never restricted to be used in that way.

    Given a Runnable you can submit it to an ExecutorService, or pass it to the constructor of Thread or you can invoke its run() method directly like you can invoke any interface method without multi-threading involved. And there are more use cases, e.g. AWT EventQueue.invokeLater(Runnable) so never expect the list to be complete.

    Given a Callable, you have the same options, so it’s important to emphasize that, unlike your question suggests, invoking call() directly does not involve any multi-threading. It just executes the method like any other ordinary method invocation.

    Since there is no constructor Thread(Callable) using a Callable with a Thread without an ExecutorService requires slightly more code:

    FutureTask<ResultType> futureTask = new FutureTask<>(callable);
    Thread t=new Thread(futureTask);
    t.start();
    // …
    ResultType result = futureTask.get(); // will wait for the async completion
    
    0 讨论(0)
  • 2021-02-02 11:43

    The simple direct answer is that you need to use an ExecutorService if you want to use a Callable to create and run a background thread, and certainly if you want to obtain a Future object, or a collection of Futures. Without the Future, you would not be able to easily obtain the result returned from your Callable or easily catch Exceptions generated. Of course you could try to wrap your Callable in a Runnable, and then run that in a Thread, but that would beg the question of why, since by doing so you would lose much.


    Edit
    You ask in comment,

    Do you mean like the code below, which works?

    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();
                }
            }
        }
    }
    

    My reply: Yes. The code in the link "sort of" works. Yes, it creates background threads, but the results from the calculations performed in the Callables are being discarded, and all exceptions are being ignored. This is what I mean by "since by doing so you would lose much".


    e.g.,

      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();
    

    Edit 2
    Or if you want the results returned as they occur, use a CompletionService to wrap your ExecutorService, something I've never attempted before:

    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!");
       }
    }
    
    0 讨论(0)
  • 2021-02-02 11:46

    Yes you can use the call() method of a Callable or the run() method of a Runnable from your own thread directly. However this should be your last resort in special circumstances (for example integrating legacy code or unit tests). Scanners might detect this and alert you about a possible architectural problem, so it is better to not do it.

    You could also use your own ExecutorService (or use Guava's MoreExecutors.sameThreadExecutor()) which does basically the calling in the invoking thread. This will isolate your "unclean" usage of the interface to this Executor and allow it to use a different Executor whenever you want.

    BTW: be careful, when you inherit from Thread, you should never use it without start/stop as that might lead to a leak. This is one of the reasons why bug scanners alert on calling run() methods directly.

    0 讨论(0)
提交回复
热议问题