2014-05-20 81 views
1

這個問題觸發一用戶可見的例外是Exception in finalize method和類似的問題相反。在Finalize方法

我創建一個AutoCloseable類構成了嚴重的風險,如果沒有正確關閉。在這種情況下,我正在尋求失敗 - 以便用戶不會意外地忘記這麼做。

我意識到並同意,一般最佳做法是讓Closeable優雅地失敗並盡其所能緩解來電者的錯誤,但在這種情況下,來電者不會想錯過此操作。如果你在概念上不同意這個想法,我會很感激你的反饋,但在這種情況下,考慮一個關於Java內部的學術練習的問題。

我想如果我調用IllegalStateException並打斷用戶,如果我的類的finalize()方法被調用,並且實例還沒有被清理。但finalize()顯式吞下未捕獲的異常,這使得這個棘手。導致用戶從finalize()方法中看到的RuntimeException的最佳方法是什麼?

這裏有一個演示類的東西,我這麼遠:

public class SeriouslyCloseable implements AutoCloseable { 
    // We construct an Exception when the class is initialized, so that the stack 
    // trace informs where the class was created, rather than where it is finalized 
    private final IllegalStateException leftUnclosed = new IllegalStateException(
     "SEVERE: "+getClass().getName()+" was not properly closed after use"); 
    private boolean safelyClosed = false; 

    @Override 
    public void close() { 
    // do work 
    safelyClosed = true; 
    } 

    @Override 
    protected void finalize() throws IllegalStateException { 
    if(!safelyClosed) { 
     // This is suppressed by the GC 
     throw leftUnclosed; 
    } 
    } 
} 

注:我知道也是finalize()不能保證運行,所以任何事情我身邊這個方法實施並非絕對不會發生。我仍然喜歡,如果GC給我們機會,可能會發生。

回答

1

你不能強迫的這種方法是通過任意的,實現相關Thread執行,目前還不清楚其中Thread異常應提高從finalize方法拋出異常。

即使您已經知道了哪個線程是針對的,爲什麼Thread.stop(Throwable)已被棄用(並且自Java 8以來不支持):導致線程在任意代碼位置拋出任意throwable可能會導致很多的傷害。例如。錯過了線程即將進入的另一個close()操作。此外,在調用finalize方法時,導致錯誤的線程可能不再活動。


在它不是投擲結束,但報告要實現一個例外。你可以模仿的原始的,未抑制的行爲是這樣的:

@Override 
protected void finalize() throws Throwable { 
    if(!safelyClosed) { 
     final Thread t = Thread.currentThread(); 
     t.getUncaughtExceptionHandler().uncaughtException(t, leftUnclosed); 
    } 
} 

默認情況下,它會打印出異常堆棧跟蹤到控制檯。手動呼叫printStackTrace的優勢在於它與可能安裝的應用程序特定異常處理程序一起工作:

import java.util.logging.Level; 
import java.util.logging.Logger; 

public class ThrowableInFinalize { 
    public static void main(String[] args) throws InterruptedException { 
    Thread.setDefaultUncaughtExceptionHandler(
              new Thread.UncaughtExceptionHandler() { 
     public void uncaughtException(Thread t, Throwable e) { 
     Logger.getLogger("ThrowableInFinalize") 
       .log(Level.SEVERE, "uncaught exception", e); 
     } 
    }); 
    new ThrowableInFinalize(); 
    System.gc(); 
    Thread.sleep(1000); 
    } 

    private final IllegalStateException leftUnclosed = new IllegalStateException(
     "SEVERE: "+getClass().getName()+" was not properly closed after use"); 
    private boolean safelyClosed; 
    @Override 
    protected void finalize() throws Throwable { 
    if(!safelyClosed) { 
     final Thread t = Thread.currentThread(); 
     t.getUncaughtExceptionHandler().uncaughtException(t, leftUnclosed); 
    } 
    } 
} 
1

一種選擇是簡單地終止JVM徹底:

@Override 
protected void finalize() throws IllegalStateException { 
    if(!safelyClosed) { 
    leftUnclosed.printStackTrace(System.err); 
    System.exit(255); 
    } 
} 

下相當一致地複製所需的行爲,包括顯示沒有結束的Closeable創建其中的跟蹤:

private static void resourceLeak() { 
    SeriouslyCloseable sc = new SeriouslyCloseable(); 
    //sc.close(); 
} 

public static void main(String[] args) throws InterruptedException { 
    resourceLeak(); 
    System.gc(); 
    Thread.sleep(1000); 
    System.out.println("Exiting Normally"); 
} 
java.lang.IllegalStateException: SEVERE: SeriouslyCloseable was not properly closed after use 
     at SeriouslyCloseable.<init>(SeriouslyCloseable.java:5) 
     at SeriouslyCloseable.method(SeriouslyCloseable.java:23) 
     at SeriouslyCloseable.main(SeriouslyCloseable.java:28) 
0

不要在敲定時拋出異常。

那麼如何才能溝通那個嚴重的編程錯誤呢?你可以登錄它。但是,只有當有人閱讀該日誌時纔有用。

你可以翻轉一些標誌,這將使整個應用程序無法使用(或至少你的庫) - 存儲在靜態字段例外(最初爲空),並在一些操作扔它,如果它被設置。爲了在JVM關閉的情況下生存下來,您可以將它寫入文件(但有時您不能),並在啓動時加載它並恢復投擲(直到此文件被刪除並重新啓動應用程序)。

你可以關閉JVM(如dimo414我之前提出的建議),但相同的應用程序服務器上的其他應用程序也不會感謝你,這將防止關閉其他資源。

你可以發送一些信息在其他地方(例如,通過HTTP或JMS),但這需要在其他地方是傾聽和記錄相比少忽略。

而且你可以實現多種選擇如何處理它,並允許用戶選擇。