2016-08-30 60 views
3

我們有一個服務方法GetDataParallel(),它可能被許多客戶端當前調用,我們使用ExecutorService來調用它內部的MyCallable。但是我發現,除非我調用executorService.shutdown();應用程序永遠不會退出,那麼爲什麼應用程序不能退出,我們必須在應用程序退出之前手動關閉所有線程池線程?在服務環境中,我認爲我們不需要調用executorService.shutdown();保持應用程序活着,對嗎?當executorService.shutdown();應該叫

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class MultiThreading { 

    static ExecutorService executorService = Executors.newFixedThreadPool(100); 
    private List<String> _BusinessUnits= new ArrayList<String>(); 
    public static void main(String[] args) throws Exception { 

     MultiThreading kl =new MultiThreading(); 
     kl.GetDataParallel(); 
     Thread.sleep(10000); 
     System.out.println("111111111"); 
     //executorService.shutdown(); 

    } 

    public void GetDataParallel() throws Exception 
    { 
     _BusinessUnits.add("BU1"); 
     _BusinessUnits.add("BU2"); 
     _BusinessUnits.add("BU3"); 

     for(final String v : _BusinessUnits) 
     { 
      ExecutorServiceTest.executorService.submit(new MyCallable()); 
     } 
    } 
} 

    class MyCallable implements Callable { 
    @Override 
    public String call() throws Exception { 
     Thread.sleep(1000); 
     //return the thread name executing this callable task 
     System.out.println(Thread.currentThread().getName()); 
     return Thread.currentThread().getName(); 
    } 
} 
+0

執行程序線程不是守護線程,因此當他們正在運行的JVM將不會終止。您必須手動關閉它們。 http://stackoverflow.com/questions/2213340/what-is-daemon-thread-in-java我想你可以使用提供守護線程的自定義ThreadFactory,但關機更直接,更清潔,更安全,特別是如果你在做你的任務。 –

+0

@Jason C,「跑步時」的含義是什麼?運行意味着執行代碼的線程,或者它們只停留在線程池中?你知道線程在1秒後完成了可調用,因此1秒後它不應該處於運行狀態,但是應用程序在很多時間後仍然不會退出。 – Jason

+0

如果它們處於活動狀態,則在線程池中。不一定執行任務。例如。即使隊列爲空,固定線程池中的線程也始終運行。如果沒有任務,高速緩存線程池中的線程最終會閒置並死掉。調用關機將終止線程。 –

回答

1

Java中有兩種線程(當然取決於你如何看待它們)。 '用戶'線程和'守護進程'線程。您應用的下列情形之一的結束:

  1. 你叫System.exit()
  2. 你沒有User線程留在你的應用程序。這解釋here。您main功能由JVM上的「用戶」的線程中執行,這意味着,只要你還沒有完善自己main功能

注意。大多數多線程應用程序將只運行主函數來啓動所有需要的線程。

Daemon線程背後的想法是你可以做一些事情(定期),但是如果所有其他任務都完成了,它不會阻止應用程序退出。

默認情況下,新線程爲「非守護進程」線程,對於由ExecutorService開發的線程也是如此。如果你想改變這一點,你必須創建自己的ThreadFactory。 A ThreadFactory允許您手動爲您的ExecutorService創建線程,它將在ExecutorService需要新線程時調用。下面是創建「守護進程」線程中的一個例子:

public class DaemonThreadFactory implements ThreadFactory 
{ 

    @Override 
    public Thread newThread(final Runnable r) 
    { 
     Thread t = new Thread(r); 
     t.setDaemon(true); 
     return t; 
    } 
} 

這可以再通過創建執行服務使用:

ExecutorService service = Executors.newFixedThreadPool(100, new DaemonThreadFactory()); 

注意,這也是定製給你的線程的方式名稱,這是非常有用的,因爲許多logframeworks記錄線程名稱(並且調試器顯示它)。

如果你這樣做,在你的應用程序中,它會立即退出,因爲你只創建'守護進程'線程,所以你將不得不保持另一個線程活着(這可以由另一個框架隱式完成,例如,如果你有GUI)。

另一種方法是手動調用System.exit()。不建議在您的代碼中通常撥打System.exit()。主要是因爲它不允許重構,重用,測試和許多退出點使您的應用程序變得不可預測。爲了規避這些問題,您可以創建一個處理作業完成事件的回調函數。在此應用程序中,您可以撥打System.exit(),您的測試代碼或其他應用程序可以執行其他操作。

+0

如果所有100個線程都掛起而沒有返回,則表示沒有線程可以爲新請求提供服務,那樣的話,我們可以強制中止掛起線程嗎?或者如果有超時設置,並且在超時之後掛起線程將退出\中止並自動返回線程池。或者還有其他好的建議來處理掛起線程? – Jason

+0

@Jason沒有很好的方法來中止一個真正卡住的線程(如果你想知道更多,請閱讀爲什麼'Thread.stop()'已被棄用)。你可以讓你的任務中斷(意味着他們必須檢查'Thread.interrupted()'標誌並正常處理'InterruptedException'。 – Thirler

1

在應用環境中,必須調用關機,以確保ExecutorService推出必須停止線程,它不應該接受更多的新任務。否則JVM不會退出。

如果是服務,您應該在停止執行服務之前調用shutdown。對於例如在網絡應用的情況下,ServletContextListenercontextDestroyed方法對於調用的shutdown方法是有用的。這將確保任何現有任務必須在突然終止應用程序之前完成,但不會接受任何新任務處理。

+0

,如果是Service,自定義ThreadFactory帶守護進程線程,或者ExecutorService不帶守護進程線程,哪一個更適合?或者完全取決於? – Jason

+0

ThreadFactory是爲了避免調用'new Thread'並使代碼混亂。對於命名線程和設置優先級也更加有用。在服務的情況下,如果要控制併發線程數(但它們的名稱和優先級沒有區別),請使用ExecutorService。 – CuriousMind

4

通常,當應用程序正在退出時關閉ExecutorService - 大多數應用程序都有某種生命週期和關閉順序。如果你期望你的應用程序在主線程完成它所要做的任何事情之後退出,那麼當你的應用程序完成時你想關閉它(這意味着它有一個明確的工作完成點,並且你可以知道什麼時候該完成是)。

服務器端框架經常有生命週期方法,您可以將其鎖定以檢測代碼何時關閉並完全退出。或者你可以使用虛擬機關機掛鉤(可能會延遲關閉,直到所有當前排隊的作業都完成),這樣無論代碼如何導致程序退出,你的清理代碼都會運行。

一般來說,如果框架沒有提供一個良好定義的出口點是一個好主意 - 它可以讓你有一個可以乾淨地卸載的應用程序(也可以用更新後的配置重新加載)沒有虛擬機必須關閉 - 我已經使用了這個技巧,以便能夠讓服務器應用程序重新配置自身,並以零宕機時間重新加載以響應Unix信號。

因此,對您的問題的簡單回答是「什麼時候不再使用」。在幾乎任何應用程序中,都有一個明確無誤的點。

BTW,違背了其他響應者之一,一個ExecutorService 可以使用守護線程 - 你可以提供配置,但是你想你開始之前線程的ThreadFactory。但是我鼓勵你不要使用守護進程線程,並且在明確定義的點上明確關閉線程池 - 這不是典型的做法,但這意味着你的代碼有可能被徹底關閉,並且它會鼓勵你考慮生命週期,這可能會導致更好的代碼。

+1

好了解釋@Tim。 – CuriousMind

+0

@Tim,非常感謝你的解釋。 – Jason

+0

「如果你希望你的應用程序在主線程完成它所要做的任何事情時退出,那麼當它的工作完成時你想關閉它」好吧說:) –

1

但是我發現,除非我調用了executorService.shutdown();應用程序永遠不會退出,

我認爲我們不需要調用executorService.shutdown();保持應用程序活着,對嗎?

是的。應用程序是活着的,除非您致電executorService.shutdown()

每個應用程序都應該有一個退出點。您可以在該出口點調用關機。

如果您沒有任何已知的退出點,ShutdownHook是解決您的問題的一種方法。但是,你應該知道,

在極少數情況下,虛擬機可能會中止,也就是停不乾淨

關停對於您所引述的例子運行時,您可以通過多種方式解決這個問題(invokeAll, Future.get(), CountDownLatch等)。看看相關的SE問題。

ExecutorService, how to wait for all tasks to finish