簡體   English   中英

如何在已關閉的ScheduledExecutorService中保持線程等待

[英]How to keep thread waiting in ScheduledExecutorService that has been shutdown

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(),nonDaemon線程在執行后永遠不會被垃圾收集。 有沒有辦法可以確保線程在執行前始終處於等待狀態? 或者我可以使用其他替代方法來確保:

  • 我可以在執行程序服務中運行的線程的名稱附加前綴(這實際上是DefaultThreadFactory實現的功能)
  • 非守護程序線程在執行后獲取GC。
  • 創建的線程保持等待狀態,直到它們運行為止。

線程沒有等待因為你沒有調用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

我希望它對你有所幫助!

您所看到的是ScheduledExecutor委托給普通的ThreadPoolExecutor以獲取關閉功能。 關閉TPE時,其所有線程都會在后備工作隊列中旋轉,直到它為空。 那么ScheduledThreadPool使用的DelayedQueue可能不是空的,但是如果你進行了輪詢,你會得到一個空值,因為任務還沒有准備好安排。 它會旋轉並旋轉直到准備好

你唯一能做的就是shutdownNow並執行以其他方式返回的任務。

我也被告知這實際上是在Java 7中修復的

找出一個解決方案:它是shutdown(),它將所有待處理任務線程的狀態從“等待”更改為“正在運行”。 我現在使用executorService來調度自己的shutdown(),而不是立即調用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;
}

跑你的代碼。 這是來自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

現在,這是來自於ThreadPoolExecutor.java:950 grepcode:

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

這讓我得出結論,問題出在您的測試代碼中 - 您正在關閉執行程序太快,並且線程發現自己處於一種奇怪的狀態。 這不應該是生產代碼中的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM