2012-07-19 42 views
3

我有一個簡單的java程序,它創建一系列存儲在本地tmp目錄中的臨時文件。我已經添加了一個簡單的關閉鉤子,它遍歷所有文件並刪除它們,然後在退出程序之前刪除tmp目錄。這裏是代碼:在關機鉤中斷Java中的所有線程

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 
    @Override 
    public void run() { 
     File tmpDir = new File("tmp/"); 
     for (File f : tmpDir.listFiles()) { 
      f.delete(); 
     } 
     tmpDir.delete(); 
    } 
})); 

我的問題是,創建這些文件的線程可能不會在啓動關閉掛鉤的終止,因此,有可能是listFiles()後創建的文件被調用。這會導致tmp目錄不會被刪除。我想出瞭解決此2名黑客:

哈克#1:

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 
    @Override 
    public void run() { 
     File tmpDir = new File("tmp/"); 
     while (!tmp.delete()){ 
       for (File f : tmpDir.listFiles()) { 
       f.delete(); 
      } 
     } 
    } 
})); 

哈克#2:

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 
    @Override 
    public void run() { 
     try{ 
      Thread.sleep(1000); 
     } catch(InterruptedException e){ 
      e.printStackTrace(); 
     } 
     File tmpDir = new File("tmp/"); 
     for (File f : tmpDir.listFiles()) { 
      f.delete(); 
     } 
      tmpDir.delete(); 
     } 
})); 

也不是一個特別好的解決方案。最理想的是讓shutdown hook等待,直到所有線程都已經終止,然後再繼續。有誰知道這是否可以做到?

+4

什麼關於讓每個線程清理自己的臨時文件?我發現有相同的類負責創建和刪除自己的臨時文件很有幫助。 – 2012-07-19 19:04:17

+0

['File.deleteOnExit()'](http://docs.oracle.com/javase/6/docs/api/java/io/File.html#deleteOnExit())會得到什麼結果?似乎「正常終止」要求同樣適用於關閉鉤子執行。 – erickson 2012-07-19 19:05:08

+0

@erickson'File.deleteOnExit();'適用於子文件,但不適用於tmp目錄。大概原因是不能保證在目錄試圖刪除之前刪除所有的子文件。這導致一個非空目錄,這是java不能刪除的目錄。 – ewok 2012-07-19 19:46:08

回答

7

只需跟蹤所有正在運行的線程,然後在關閉該程序之前跟蹤他們即可.join()

這是一個問題的答案標題伊渥克說,他不能使用.deleteOnExit()

+0

這是一個非常糟糕的主意。幾乎可以保證中斷不終止。 – 2014-06-05 14:00:31

+0

對不起,我不明白你的意思。你能否詳細說明或提供你自己的答案,關於如何正確地做到這一點? – 2014-06-05 16:08:22

+0

簡短回答:在全局可變狀態的存在下,線程是一個可怕的併發抽象,並且應該在火中死亡。如果任何一個線程在代碼中不檢查中斷或正確處理它們(我在生產中看到的Java代碼的很大一部分),那麼調用** .add()**將永遠阻塞。最好加入超時並且無法刪除臨時文件。有一個更好的解決方案,我已發佈。 – 2014-06-07 01:37:15

2

泰勒說什麼,但有一點更詳細:

  • 保持到線程,其中引用關機掛鉤可以訪問它們。
  • 在線程上有關閉掛接調用中斷。
  • 檢查線程的代碼,以確保它們實際上對中斷做出響應(而不是吃掉InterruptedException和blundering,這是很多代碼的典型代碼)。中斷應提示線程停止循環或阻塞,結束未完成的業務並終止。
  • 對於每一個你不想繼續下去的線程,直到它完成爲止,檢查線程是否還活着,如果是這樣的話,調用它加入,設置超時以防它在合理的時間內沒有完成,在這種情況下您可以決定是否刪除文件。
+0

你說的大部分內容都是通過加入完成的,但是絕對有幫助。 – 2012-07-20 16:14:44

2

UPDATE:Tyler Heiks準確地指出deleteOnExit()不是一個有效的解決方案,因爲OP試了一下,並沒有奏效。我正在提供一個備用解決方案。它又是間接的,但主要是因爲使用線程和ShutdownHook的原始設計是致命的缺陷。

使用終於塊刪除臨時文件。

依賴關閉資源管理的鉤子是一個非常糟糕的主意,並且使代碼非常難以在較大的系統中編寫或重用。將資源從一個線程轉移到另一個線程是一個更糟的想法。像文件和流這樣的資源是線程間共享最危險的東西。從這裏獲得的可能性很小,對於每個線程使用庫的createTempFile方法獨立獲取臨時文件並使用嘗試/最終來管理它們的使用和刪除將更有意義。

與臨時文件處理系統上的慣例是把它們看作塊盒,其中:在磁盤上的

  1. 位置是不透明的(不相關的,而不是由程序直接使用)
  2. 文件名是無關緊要的
  3. 文件名是保證互斥

上述第三是很難實現的,如果你手工卷代碼來創建和命名臨時FIL你自己。它很可能是脆弱的,並在最糟糕的時候失敗(每個人有3AM尋呼機?)。

您提供的算法可能會刪除由巧合共享相同父目錄的其他進程創建的文件。對於其他計劃的穩定性來說,這不太可能是一件好事。

這裏的高級過程:

  1. 獲取路徑Files.createTempFile()(或與傳統的預Java 7的代碼文件File.createTempFile())然而
  2. 使用臨時文件所需
  3. 刪除文件

這與InputStream或其他需要手動管理的資源類似。

,對於顯式的資源管理的一般模式(當AutoCloseable試穿與資源不可用)如下。

Resource r = allocateResource(); 
try { 
    useResource(r); 
} finally { 
    releaseResource(r); 
} 

路徑的情況下,它看起來是這樣的:

Path tempDir = Paths.get("tmp/); 
try { 
    Path p = Files.createTempFile(tempDir, "example", ".tmp"); 
    try { 
     useTempFile(f); 
    } finally { 
     Files.delete(f); 
    } 
} finally { 
    Files.delete(tempDir); 
} 

在預Java 7的遺產,與文件使用看起來像這樣:

File tempDir = new File("tmp/"); 
try { 
    File f = File.createTempFile(tempDir, "example", ".tmp"); 
    try { 
     useTempFile(f); 
    } finally { 
     if (!f.delete()) { 
      handleFailureToDeleteTempFile(f); 
     } 
    } 
} finally { 
    if (!tempDir.delete()) { 
     handleFailureToDeleteTempDir(tempDir); 
    } 
} 
+0

OP聲明deleteOnExit()不是一個選項,並且不起作用,因此我們提供了一種替代方法。另外,我正在回答標題中的問題。 – 2014-06-07 09:40:48

+0

Touché。我沒有閱讀評論。這是手動管理臨時子目錄的一個很好的例子。使用單個臨時文件並根據需要使用內存數據結構模擬層次結構可能會更好。 – 2014-06-08 02:24:00

+0

你介意在我的原始答案上逆轉倒票嗎? – 2014-06-08 05:23:50