1

この質問は、finalize メソッドの例外および同様の質問の反対です。

AutoCloseable適切に閉じないと重大なリスクをもたらすクラスを作成しています。そのような場合に、ユーザーがうっかり忘れないように、フェイル ハードを探しています。

一般的なベスト プラクティスは、Closeables が正常に失敗し、発信者のエラーを軽減するために最善を尽くすことですが、この場合、発信者はこれを見逃すことはありません。概念的にこの考えに同意しない場合は、フィードバックをいただければ幸いです。ただし、その場合、質問は Java の内部に関する学術的な演習と考えてください。

私が想像しているのは、クラスのメソッドが呼び出され、インスタンスがまだクリーンアップされていないIllegalStateException場合に、ユーザーを発生させて中断することです。finalize()ただしfinalize()、キャッチされていない例外を明示的に飲み込むため、これはトリッキーになります。RuntimeExceptionメソッドからユーザーに表示されるようにする最善の方法は何finalize()ですか?

ここに私がこれまでに得たもののデモクラスがあります:

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が機会を与えてくれるなら、私はまだそれが起こることを望んでいます.

4

3 に答える 3

1

finalizeこのメソッドは任意の実装依存によって実行され、例外が発生する場所Threadが明確でないため、メソッドから例外を強制的にスローすることはできませんThread

どのスレッドを狙うべきかがわかっていたとしても、 が非推奨 (および Java 8 以降サポートされていない) になっているのには十分な理由がありThread.stop(Throwable)ます。スレッドが任意のコードの場所で任意の 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);
    }
  }
}
于 2014-05-21T10:07:05.477 に答える
0

ファイナライズで例外をスローしません。

では、重大なプログラミング エラーが存在することをどのように伝えることができるのでしょうか? あなたはそれを記録することができました。しかし、それは誰かがそのログを読むときだけ役に立ちます。

アプリ全体(または少なくともライブラリ)を使用不可にするフラグを反転できます-その例外を静的フィールド(元はnull)に保存し、設定されている場合は何らかの操作でそれをスローします。JVM のシャットダウンを乗り切るには、ファイルに書き込み (ただし、できない場合もあります)、開始時にロードして、スローを再開することができます (そのようなファイルが削除され、アプリケーションが再起動されるまで)。

JVM をシャットダウンすることはできますが (dimo414 が以前に提案したように)、同じアプリ サーバー上の他のアプリケーションからは感謝されず、他のリソースを閉じることができなくなります。

別の場所 (例: http や JMS 経由) にメッセージを送信することもできますが、それには別の場所でリッスンし、ログよりも無視されないようにする必要があります。

また、それを処理する方法を複数のオプションを実装して、ユーザーが選択できるようにすることもできます。

于 2014-05-20T14:38:55.093 に答える