5
public ScheduledFuture<?> executeTaskWithDelay(String name, 
      final Runnable runnable, Period delay, boolean isDaemon) {
  ScheduledExecutorService executorService = 
        Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
          name, isDaemon));
  ScheduledFuture<?> future =  executorService.schedule(runnable, 
        delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);
  executorService.shutdown(); 
  return future;
}

アプリケーションのプロファイリングを行ったところ、このメソッドによって作成されたスケジュールされたスレッドは、実行される前に常に「待機中」ではなく「実行中」の状態になっていることに気付きました。executorService.shutdown() を削除すると、必要なことが行われます (つまり、スレッドが実行されるまで待機状態のままになります)。ただし、executorService.shutdown() を使用しないと、デーモン以外のスレッドが実行後にガベージ コレクションされることはありません。実行前にスレッドが常に待機状態であることを確認する方法はありますか? または、次のことを保証するために、このメソッドに使用できる他の代替品は何ですか?

  • エグゼキューター サービスで実行されるスレッドの名前にプレフィックスを付けることができます (これは、実質的に DefaultThreadFactory 実装が行うことです)。
  • 非デーモン スレッドは、実行後に GC を取得します。
  • 作成されたスレッドは、実行する時間になるまで待機状態のままになります。
4

4 に答える 4

3

future.get() メソッドを呼び出さなかったため、スレッドは待機していません。それを証明するために単体テストを行いました。

テスト #1 (future.get() メソッドを呼び出さない):

@Test
public void testSchedule() throws InterruptedException, ExecutionException {

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    System.out.println(new Date());
    ScheduledFuture<?> future =  executorService.schedule(new Runnable() {

        public void run() {
            System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");                
        }

    }, 5, TimeUnit.SECONDS);

    //System.out.println("future : " + future.get());

    executorService.shutdown();
    System.out.println(new Date());
}

出力は次のとおりです。

Thu May 24 10:11:14 BRT 2012
Thu May 24 10:11:14 BRT 2012

テスト #2 (future.get() メソッドの呼び出し):

@Test
public void testSchedule() throws InterruptedException, ExecutionException {

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    System.out.println(new Date());
    ScheduledFuture<?> future =  executorService.schedule(new Runnable() {

        public void run() {
            System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");                
        }

    }, 5, TimeUnit.SECONDS);

    System.out.println("future : " + future.get());

    executorService.shutdown();
    System.out.println(new Date());
}

出力は次のとおりです。

Thu May 24 10:12:48 BRT 2012
8 - pool-1-thread-1 - Executing thread...
future : null
Thu May 24 10:12:53 BRT 2012

お役に立てば幸いです。

于 2012-05-24T13:14:25.857 に答える
2

表示されているのは、シャットダウン機能のためにプレーンなThreadPoolExecutorに委任しているScheduledExecutorです。TPEをシャットダウンすると、TPEが空になるまで、すべてのスレッドがバッキングワークキューでスピンします。ScheduledThreadPoolはDelayedQueueを使用しますが、これは空ではない可能性がありますが、ポーリングすると、タスクをスケジュールする準備ができていないため、nullが返されます。準備ができるまで回転します

実際にできることは、shutdownNowを実行して、他の方法で返されるタスクを実行することだけです。

また、これは実際にはJava7で修正されていると言われています

于 2012-05-24T12:49:52.170 に答える
2

解決策を考え出した:保留中のすべてのタスク スレッドの状態を「待機中」から「実行中」に変更するのは shutdown() です。shutdown() をすぐに呼び出すのではなく、executorService を使用して、独自の shutdown() への呼び出しをスケジュールします。これにより、保留中のタスクが可能な限り長く待機状態に留まることが保証され、CPU リソースが節約されます。

public ScheduledFuture<?> executeTaskWithDelay(String name, 
      final Runnable runnable, Period delay, boolean isDaemon) {
  final ScheduledExecutorService executorService =  
        Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
      name, isDaemon));
  ScheduledFuture<?> future =  executorService.schedule(runnable, 
        delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);

  executorService.schedule(new Runnable() {
      @Override
      public void run() {
        executorService.shutdown();
      }}, delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);

  return future;
}
于 2012-05-24T19:27:09.993 に答える
0

コードを実行します。これは、jvisualvm のスレッド ダンプからのものです (スレッドに「bla」という名前を付けました)。

"bla" prio=5 tid=7fa2bc16a000 nid=0x10bd53000 runnable [10bd52000]
  java.lang.Thread.State: RUNNABLE
  at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:950)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
  at java.lang.Thread.run(Thread.java:680)

  Locked ownable synchronizers:
    - None

さて、これはそのためのgrepcodeからのものですThreadPoolExecutor.java:950

944         // It is possible (but unlikely) for a thread to have been
945         // added to workers, but not yet started, during transition to
946         // STOP, which could result in a rare missed interrupt,
947         // because Thread.interrupt is not guaranteed to have any effect
948         // on a non-yet-started Thread (see Thread#interrupt).
949         if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
950             t.interrupt();

これにより、問題はテスト コードにあると結論付けられます。エグゼキューターのシャットダウンが速すぎて、スレッドが奇妙な状態になっていることがわかります。これは、製品コードでは問題になりません。

于 2012-05-24T12:34:47.580 に答える