4

ローカルのtmpディレクトリに保存された一連の一時ファイルを作成する単純なJavaプログラムがあります。プログラムを終了する前に、すべてのファイルをウォークスルーして削除し、次にtmpディレクトリを削除する単純なシャットダウンフックを追加しました。コードは次のとおりです。

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();
        }
}));

どちらも特に良い解決策ではありません。理想的なのは、すべてのスレッドが終了するまでシャットダウンフックを待機させてから続行することです。これができるかどうか誰かが知っていますか?

4

3 に答える 3

7

.join()プログラムをシャットダウンする前に、実行中のすべてのスレッドを追跡してから、それらを追跡してください。

これは、イウォークが使用できないと言っている質問のタイトルに対する回答です。.deleteOnExit()

于 2012-07-19T19:05:27.770 に答える
2

タイラーが言ったことですが、もう少し詳しく説明します。

  • シャットダウンフックがスレッドにアクセスできるスレッドへの参照を保持します。
  • スレッドでシャットダウンフック呼び出し割り込みを発生させます。
  • スレッドのコードをレビューして、実際に割り込みに応答することを確認します(InterruptedExceptionを食べて失敗するのではなく、多くのコードで一般的です)。割り込みは、スレッドにループまたはブロックを停止し、未完了のビジネスをまとめて終了するように促す必要があります。
  • 終了するまで続行したくないスレッドごとに、スレッドが稼働しているかどうかを確認し、稼働している場合は、joinを呼び出し、妥当な時間内に終了しない場合に備えてタイムアウトを設定します。この場合、スレッドを決定できます。ファイルを削除するかどうか。
于 2012-07-20T14:01:44.063 に答える
2

更新: Tyler Heiksは、OPが試したが機能しなかったため、deleteOnExit()は有効なソリューションではないことを正確に指摘しました。私は別の解決策を提供しています。これも間接的ですが、主にスレッドとShutdownHookを使用した元の設計に致命的な欠陥があるためです。

一時ファイルを削除するには、 finallyブロックを使用します。

リソース管理をShutdownHooksに依存することは非常に悪い考えであり、大規模なシステムでコードを作成または再利用することを非常に困難にします。スレッドからスレッドにリソースを渡すことはさらに悪い考えです。ファイルやストリームなどのリソースは、スレッド間で共有するのに最も危険なものの1つです。これから得られるものはほとんどない可能性があり、各スレッドがライブラリのcreateTempFileメソッドを使用して一時ファイルを個別に取得し、 try/finallyを使用してそれらの使用と削除を管理する方がはるかに理にかなっています。

システム上の一時ファイルを処理するための規則は、次の場合にそれらをブロックボックスとして扱うことです。

  1. ディスク上の場所が不透明です(プログラムとは無関係であり、プログラムによって直接使用されません)
  2. ファイル名は関係ありません
  3. ファイル名は相互に排他的であることが保証されています

上記の3つ目は、コードを手動でロールして一時ファイルを作成し、自分で名前を付ける場合、実現するのが非常に困難です。最悪の場合、脆弱で失敗する可能性があります(午前3時のポケットベルは誰ですか?)。

提示するアルゴリズムは、同じ親ディレクトリを偶然に共有する他のプロセスによって作成されたファイルを削除する可能性があります。それはそれらの他のプログラムの安定性にとって良いことではないでしょう。

高レベルのプロセスは次のとおりです。

  1. Files.createTempFile()を使用してパスを取得します(またはFile.createTempFile()を使用してJava7より前のレガシーコードファイル使用します)
  2. 必要に応じて一時ファイルを使用する
  3. ファイルを削除する

これは、手動で管理する必要があるInputStreamまたはその他のリソースに似ています。

明示的なリソース管理の一般的なパターン(AutoCloseableおよびtry-with-resourcesが使用できない場合)は次のとおりです。

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での使用は次のようになります。

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);
    }
}
于 2014-06-07T01:33:05.533 に答える