web-dev-qa-db-ja.com

ExecutorServiceなしで呼び出し可能スレッドを使用できますか?

ExecutorServiceなしで呼び出し可能スレッドを使用できますか? ExecutorServiceなしで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 {
       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を使用すると、コードは少数のスレッドで動作します。私の間違いはどこですか?

18
user3233853

interfacesは、多くの場合、意図した使用例で作成されますが、そのような使用法に制限されることはありません。

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
33
Holger

単純な直接的な答えは、Callableを使用してバックグラウンドスレッドを作成および実行する場合、およびFutureオブジェクトまたはFuturesのコレクションを取得する場合は、ExecutorServiceを使用する必要があるということです。 Futureがないと、Callableから返された結果を簡単に取得したり、生成された例外を簡単にキャッチしたりできません。もちろん、CallableをRunnableでラップしてから、それをスレッドで実行しようとすることもできますが、そうすることで多くを失うことになるので、なぜかという疑問が生じます。


編集
あなたはコメントで尋ねます、

機能する以下のコードが好きですか?

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

私の返事:はい。 「一種の」リンクのコードは機能します。はい、バックグラウンドスレッドを作成しますが、Callableで実行された計算の結果は破棄され、すべての例外は無視されます。これは私が「そうすることであなたは多くを失うだろう」という意味です。


例えば。、

  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!");
   }
}

はい。自分のスレッドから、Callableのcall()メソッドまたはRunnableのrun()メソッドを直接使用できます。ただし、これは特別な状況(たとえば、レガシーコードまたは単体テストの統合)での最後の手段です。スキャナーがこれを検出して、アーキテクチャーの問題の可能性について警告する場合があるので、それを行わない方がよいでしょう。

また、基本的に呼び出しスレッドでの呼び出しを行う独自のExecutorService(またはGuavaのMoreExecutors.sameThreadExecutor()を使用)を使用することもできます。これにより、このエグゼキューターへのインターフェースの「クリーンでない」使用が分離され、いつでも別のエグゼキューターを使用できるようになります。

ところで、Threadから継承する場合は、開始/停止なしで使用しないでください。リークが発生する可能性があります。これが、バグスキャナーがrun()メソッドを直接呼び出すと警告する理由の1つです。

0
eckes
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
Prashant K S