2011-11-17 160 views
5

在我參與的開源應用程序中,我們遇到了一個錯誤,應用程序並不總是正常關閉。這就是我想要解決的問題。分析多線程Java應用程序

經驗表明,大多數情況下,線程和進程在啓動時會發生這種情況,但是沒有正確管理(例如線程正在等待套接字連接,應用程序正在關閉並且線程持續等待) 。
考慮到這一點,我已經在整個數據源中搜索了'.start()',並發現了53個事件(這嚇了我一下)。
作爲第一步,我想創建一個幫助器類(ThreadExecutor),其中當前代碼'thread.start()'將被'ThreadExecutor.Execute(thread)'替換爲:a)只有少量更改現有的源代碼和b)一個單獨的類,我可以很容易地檢查哪些線程不應該像他們應該那樣結束。要做到這一點,我想

  • 添加線程調用Execute方法
  • 時啓動線程
  • 當它結束時從activeThreads列表中刪除它執行到列表稱爲activeThreads。

這樣我就可以獲得所有執行線程的最新列表,並且當應用程序掛起時,我可以在那裏看到哪個線程正在導致它。

問題:

  • 你認爲怎麼樣的概念?我通常編碼c#,並知道如何使用.NET與工作人員一起做它,但我不太清楚Java中最好的(我想在現有源代碼中儘可能少地修改代碼)。
  • 如果這個概念似乎沒問題,我怎麼能得到一個線程終止的通知。我想避免每隔一段時間檢查一次,以瞭解activeThreads中包含的所有線程的狀態,以便在終止時刪除它們。

只是爲了澄清:之前搞清楚如何正確地終止應用程序,我要問這裏有什麼發現哪些線程是在導致某些測試用例這是非常難以複製的最佳/最簡單的方法。

+1

如果您創建線程守護進程 - 當應用程序被終止時它們不會被終止嗎? –

+0

http://download.oracle.com/javase/6/docs/api/java/lang/Thread.html#setDaemon%28boolean%29 –

+3

當應用程序未正確關閉時,是否嘗試過線程轉儲?看到什麼線程卡住了,他們在做什麼?這可能會給你一些線索。更多信息:http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ –

回答

2

嘗試使所有線程守護進程(當所有剩餘的線程都是守護進程JVM終止時)。在開始每個線程之前使用thread.setDaemon(true)

+2

這聽起來像相當於在臉上拍攝你的應用程序,所以它肯定會死。 –

+0

這不正是OP想要的嗎?線程中有一些進程不斷在某處進行監聽。該應用程序已關閉,該過程自動終止。 –

+1

這是守護線程的思想。 –

2

您可以嘗試使用jvisualvm(與jdk一起發貨,在您的jdk的bin文件夾中找到它)來查看您的應用程序。 JVisualVM可以連接到您的應用程序並顯示大量有趣的信息,包括哪些進程仍在運行。在開始你描述的道路之前,我會給你一個鏡頭。

如果您需要JVisualVM,以下是一些documentation

2

java中最好的方法是直接使用Thread pools而不是直接使用線程(雖然直接使用線程是可以接受的)。線程池接受Runnable對象,您可以將其看作任務。這個想法是,大多數線程會做一個小任務,然後結束,因爲製作一個Thread是昂貴和難以管理,你可以使用線程池,它允許像'ThreadPoolExecutor.awaitTermination()`的東西。如果您的池中的任務多於線程,則剩餘的任務將排隊。

Thread更改爲Runnable很容易,您甚至可以在自己創建的線程上執行可運行。

請注意,這可能不適用於運行很長時間的線程,但您的問題似乎表明它們最終會完成。

至於你的第二個問題,找出在某個點運行哪些線程的最好方法是在調試器(如Eclipse)中運行應用程序,並在關閉函數中暫停斷點上的所有線程。

6

我會嘗試在更改任何代碼之前分析您的應用程序的行爲。代碼更改可能會引入新的問題 - 而不是您想要解決問題時想要執行的操作。

內核關於當前正在運行哪些線程的應用程序狀態的最簡單方法是獲取線程轉儲。你說你的問題是應用程序在關機時掛起。這是應用線程轉儲的完美場景。您將能夠看到哪些線程被阻止。

你可以閱讀更多關於線程轉儲here

1

我會嘗試jprofiler的試用版或類似的東西,這會讓您對應用程序及其線程的實際操作有很多瞭解。

不要更改代碼,但請嘗試重現並瞭解何時發生此情況。

1

創建一個靜態線程池。

static ExecutorService threads = Executors.newCachedThreadPool(); 

對於螺紋變化的每一個開始:

new Thread(new AThread()).start(); 

threads.submit(new AThread()); 

當你的程序退出,列出了所有正在運行的線程:

List<Runnable> runningThreads = threads.shutdownNow(); 
for (Runnable t : runningThreads) { 
    System.out.println("Thread running at shutdown: "+t.toString()); 
} 

這不會只關閉所有正在運行的線程,它w生病列出來讓你看看他們的問題是什麼。

編輯:新增

如果你想保留的所有正在運行的線程的軌道使用:

Future f = threads.submit(new AThread()); 

,並將其存儲在列表中的某個地方。然後您可以通過以下調用瞭解其狀態:

f.isDone(); 
... etc. 
+0

高速緩存的線程池又名有史以來最可怕的發明之一。更好地確保你明白你在那裏做什麼。 – Voo

+0

同意Voo,但OP想要一些不顯眼的東西,這是非常簡單的使用,作爲一個替代不受控制的線程使用。一旦問題解決,我強烈建議他們使用一個或多個'FixedThreadPool'。 – OldCurmudgeon