2013-04-20 26 views
45

在過去幾個小時內我正在閱讀它,而且根本看不到任何理由(有效的原因)在ExecutorService上調用shutdown(),除非我們有一個龐大的應用程序存儲,幾十種和幾十種不同的執行者服務,這些服務很長一段時間都沒有使用過。在ExecutorService上調用shutdown()的原因

關機的唯一事情就是做一個正常的線程完成的工作。當正常線程完成Runnable(或Callable)的運行方法時,它將被傳遞給垃圾收集器以進行收集。使用Executor服務時,線程將被暫時擱置,不會爲垃圾收集打勾。爲此,需要關閉。

好的回到我的問題。是否有任何理由經常在ExecutorService上調用shutdown,或者在提交給它一些任務後立即調用shutdown?我想留下一個人正在做的事情,並在此之後立即致電awaitTermination(),因爲這是經過驗證的。一旦我們這樣做,我們必須重新創建一個新的ExecutorService,以做同樣的事情。 ExecutorService是不是重新使用線程的想法?那麼爲什麼要儘快銷燬ExecutorService

是不是一個合理的方式來簡單地創建ExecutorService(或對夫婦取決於你需要多少),然後在應用程序運行期間將任務傳遞給他們,然後在應用程序出口或其他應用程序重要的階段會關閉這些執行者?

我想回答一些有經驗的編程人員,他們使用ExecutorServices編寫大量異步代碼。

第二方面的問題,比較小的處理android平臺。如果你們中的一些人會說,每次關閉執行程序並不是最好的想法,並且你在android上編程,你能告訴我如何處理這些關閉(具體 - 當你執行它們時),當我們處理不同的事件時應用生命週期。

由於CommonsWare的評論,我將帖子置於中性。我真的沒有興趣爭論死亡,它似乎在那裏領先。如果他們願意分享他們的經驗,我只想了解我從有經驗的開發人員那裏詢問的內容。謝謝。

+1

「我看到很多次示例代碼,其中所有的時間,在提交或執行任務後都有shutdown()調用」 - 隨意使用超鏈接提供您的主張的證據。就我個人而言,我從來沒有見過任何符合您所述的「示例代碼」。你可能誤解了某些東西,如果我們知道你正在檢查的是什麼「示例代碼」,我們只能指出這一點。 – CommonsWare 2013-04-20 17:49:01

+2

嗨CommonsWare。首先,我看到了你的一個激進的語氣(或者看起來)對我來說,我認爲這在這裏沒有得到驗證。我並沒有試圖以負面的方式來描繪人們。至於你的引用,我主要討論Thinking In Java IV,多任務部分。你可以在Bruce Eckel的例子中找到許多這樣的例子。他們大多是簡單的,但布魯斯給我的印象從來就不是經常使用關機。無論如何,你只關注那些不屬於我文章主要部分的內容。我刪除了這些部分,因爲我真的不想爭論它。 – Lucas 2013-04-20 20:13:30

+1

hay @CommonsWare在Thinking in java中的書由Bruce Eckel在併發/執行器頁面804第四版中,他總是在簡單的應用程序中提交或執行任務後使用shutdown()方法來說明Executor如何工作,如盧卡斯所說 – Error 2015-11-12 09:36:24

回答

28

shutdown()方法執行一件事:阻止客戶端向執行程序服務發送更多工作。這意味着除非採取其他措施,否則所有現有任務仍將完成。即使對於計劃任務(例如ScheduledExecutorService),情況也是如此:計劃任務的新實例將不會運行。這可以在各種情況下有用。

假設您有一個控制檯應用程序,它具有運行N個任務的執行程序服務。如果用戶點擊CTRL-C,則預計應用程序可能會優雅地終止。這是什麼意思?也許您希望應用程序無法向執行程序服務提交更多任務,並且同時您希望等待現有的N個任務完成。你可以做到這一點使用關閉掛鉤作爲最後一招:

final ExecutorService service = ... // get it somewhere 

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 
    @Override 
    public void run() { 
     System.out.println("Performing some shutdown cleanup..."); 
     service.shutdown(); 
     while (true) { 
      try { 
       System.out.println("Waiting for the service to terminate..."); 
       if (service.awaitTermination(5, TimeUnit.SECONDS)) { 
        break; 
       } 
      } catch (InterruptedException e) { 
      } 
     } 
     System.out.println("Done cleaning"); 
    } 
})); 

這個鉤子將關閉該服務,這將阻止你的應用程序提交新的任務,並等待所有現有任務關閉前完成JVM。等待終止會阻塞5秒,如果服務關閉,則返回true。這是在一個循環中完成的,以確保服務最終會關閉。 InterruptedException每次都被吞下。這是關閉在整個應用程序中重用的執行程序服務的最佳方法。

此代碼並不完美。除非你絕對肯定你的任務最終會終止,否則你可能想等待給定的超時,然後退出,放棄正在運行的線程。在這種情況下,在最終嘗試中斷正在運行的線程(shutdownNow()也會給你一個等待運行的任務列表)的超時後調用shutdownNow()也是有意義的。如果您的任務旨在響應中斷,這將工作正常。

另一個有趣的場景是,當您有一個執行週期性任務的ScheduledExecutorService時。停止週期性任務鏈的唯一方法是致電shutdown()

編輯:我想補充一點,我不會推薦使用如上所示的一般情況下的關閉掛鉤:它可能容易出錯,應該是最後一招。而且,如果你有很多註冊的關閉鉤子,它們的運行順序是未定義的,這可能是不可取的。我寧願讓該應用程序在InterruptedException上明確地調用shutdown()

+0

對不起Giovanni遲到的迴應,並感謝你順便說一句。是的,我知道Executor的工作方式,我試圖在我的問題中解釋。關閉執行你所說的,並且它允許垃圾收集器收集那些死的線程,並且實際上收集ExecutorService實例。我的問題是具體的。在ExecutorService上提交/執行任何事情之後,是否有任何理由始終調用「shutdown()」。問題的第二部分嚴格與Android架構有關。如果以前的答案是否定的,那麼在什麼時候以及何時調用關閉。生命週期。 – Lucas 2013-12-01 10:40:08

+2

沒有理由一直調用shutdown()。事實上,這可能是絕對錯誤的做法,因爲它會阻止您再次使用執行程序服務。在服務生命週期結束時調用它的原因是,線程最終可以像你注意到的那樣被垃圾收集。如果你不這樣做,那些線程將保持JVM的活着,即使它們是空閒的。 – 2013-12-02 14:44:38

+0

「沒有理由一直調用shutdown(),實際上,這可能是絕對錯誤的做法,因爲它會阻止您再次使用執行程序服務」。這正是我的推理,以及我原來的問題中的dillema。所以要重申,問題是:我應該什麼時候在Android生命週期中關閉我的ExecutiveService? – Lucas 2013-12-02 19:12:37

3

是不是ExecutorService重用線程的整個想法?那麼爲什麼要儘快銷燬ExecutorService呢?

是的。您不應該經常銷燬並重新創建ExecutorService。在需要時(主要是在啓動時)初始化ExecutorService,並保持活動狀態,直到完成。

運行傳球給他們的任務,一旦他們一起走,然後在應用程序退出或應用程序中這難道不是一種理性的方式(取決於你需要多少或夫婦)簡單地創建ExecutorService的,然後其他一些重要階段會關閉這些執行者?

是的。在應用程序退出等重要階段關閉ExecutorService是合理的。

第二方面的問題,與android平臺有點小。如果你們中的一些人會說每次關閉執行程序並不是最好的想法,並且你在android上編程,你能告訴我在處理不同的應用程序時如何處理這些關閉(具體來說,當你執行它們時)生命週期。

假設ExecutorService在您的應用程序的不同活動中共享。每個活動都將在不同的時間間隔暫停/恢復,並且您仍然需要一個ExecutorService

而不是在Activity生命週期方法中管理ExecutorService的狀態,請將ExecutorService管理(創建/關閉)移動到您的自定義Service

創建服務ExecutorService =>onCreate()和關閉它恰當地onDestroy()

推薦關停ExecutorService方式:

How to properly shutdown java ExecutorService

0

原因上的ExecutorService

調用shutdown()

今天我遇到了我必須等到的情況在該機器上開始一系列任務之前,機器已準備就緒。

如果我沒有收到503(服務器不可用),那麼機器準備好處理我的請求,我會對此機器進行REST調用。所以,我等到第一次REST通話時獲得200(成功)。

有多種方式可以實現它,我使用ExecutorService來創建一個線程,並計劃它在每X秒後運行。所以,我需要停止的條件這個線程,檢查了這一點...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); 
    Runnable task =() -> { 
     try { 
      int statusCode = restHelper.firstRESTCall(); 

      if (statusCode == 200) { 
       executor.shutdown(); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    }; 

    int retryAfter = 60; 
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS); 

第二面的問題,採用Android平臺的有點規模較小的交易。

也許我可以回答,如果你會提供更多的上下文! 同樣根據我對Android開發的經驗,你很少需要線程。你正在開發一款需要性能線程的遊戲或應用嗎?如果不是,在Android中,您可以使用其他方法來解決像上面解釋的場景這樣的問題。您可以根據上下文使用TimerTask,AsyncTask或Handlers或Loaders。這是因爲,如果UIThread等待很長時間,您將知道發生了什麼:/

相關問題