6

ファイルとフォルダのツリーを作成しています。マルチスレッドに書き直しています。私が見る唯一の弱点は、フォルダーを作成するときです。現在、それは1つずつ(詳細に)進んでいます。ファイルを書き留める前に、パスが存在するかどうかを確認します。そうでない場合は、mkdirs を使用して不足しているすべてのものを作成します。

public void checkDir(String relativePath) {
        File file = new File(homePath + relativePath);
        if (!file.exists()) {
            if (file.mkdirs()) {
                log.info("Directory: " + homePath + relativePath + " is created!");
            } else {
                log.error("Failed to create directory: " + homePath + relativePath + " !");
            }
        }
    }

2 つのスレッドを使用するとどうなるかという質問があります。1 つにはパス A/B/C があり、もう 1 つにはパス A/B/D があります。A フォルダーのみが存在し、B フォルダーは存在しないとします。したがって、両方ともパスが存在しないことを確認し、作成したいとします。そのため、そのうちの 1 つが失敗する可能性があります。もう 1 つが高速になるからです。では、どうすればこれを管理できますか?

  1. 存在条件を削除して失敗したままにすることを考えていましたが、キャッチできる AlreadyExists 例外はありません..
  2. 最初にディレクトリ ツリーを作成します (しかし、もっと良い方法があると思いますか?)
  3. ディレクトリの作成を重要なセクションとして配置し、それを順次にします-春にこれを行う方法はわかりませんが、とにかくそれが必要であり、プロセスが遅くなりすぎないことを確認してください.

考えすぎかもしれませんが、理論的にはそのような状況が発生する可能性があります。現在は通常の Thread を使用していますが、これには spring TaskExecutor を使用したいと考えています。クリティカルセクションは自分で処理しているのですが、これはシェア変数でもなんでもなく、パスが違うので認識しないと思います。

提案をありがとう。

4

3 に答える 3

7

ディレクトリとそのFile.mkdirs()すべての親が存在しない場合は、それらを作成するメソッドが指定されます。Ergoexists(). Existence を呼び出しても意味がありませんが、とにかくチェックされます。通話exists()は時間の無駄です。mkdirs()本質的にはアトミックな操作です。考え抜こうとしても意味がありません。

の戻り値falseは必ずしも失敗ではないことに注意してください。パス内のすべてのディレクトリが既に存在していることを示しているだけかもしれません。

基本的にあなたの質問の前提は間違っています。

于 2015-04-07T09:54:34.517 に答える
5

mkdirs() がスレッドセーフかどうかの問題に対処する回答はないようです.1つの回答は、 mkdirs() がアトミックであると述べていますが、これが失敗する場合があるかもしれません. この関数は基本的にファイル システムを処理するため、それぞれのホストのオペレーティング システムへのシステム コールが含まれる可能性が高く、アプリケーションがターゲット システムを知らなければ、これらのシステム コールが実際にスレッド セーフであるかどうかを判断することは不可能になる可能性があります。に使用されます。

たとえば、mkdirs() はフォルダ構造を作成する前に存在をチェックしますが、次の場合はどうなるでしょうか。

スレッド 1 は mkdirs() を呼び出し、フォルダー構造の存在を本質的にチェックし、存在しないと判断します。その時点で、スレッド 1 が横取りされます。

スレッド 2 は mkdirs() を呼び出し、フォルダー構造の存在を本質的にチェックし、それが存在しないと判断し、続いてフォルダー構造の作成に進みます。

スレッド 1 が再び起動し、以前は存在しなかったという以前の判断でフォルダー構造の作成を試行し続けます。

そこでは何が起こりますか?わかりませんが、この一連のイベントをテストするのは難しく、特に create folder システム コールがオペレーティング システムによって異なることを知っていると難しいでしょう。スレッドの安全性を確保し、追跡とデバッグが困難になる可能性のあるエラーの発生を回避するための最善の策は、コードのこの重要なセクションにある程度の相互排除を実装することです。

素朴なアプローチを取り、両方のスレッドがアクセスできる単一の「グローバル」変数、たとえばブール値 b を宣言してから、クリティカルセクションの周りに次のコードを追加するのは簡単だと思います。

synchronized(b) {
     // Your critical section here
}

これにより、一方のスレッドが b をロックした場合、もう一方のスレッドが待機している間、単独でクリティカル セクションにアクセスできるようになり、両方のスレッドから mkdir() が呼び出されないことが保証されます。

ただし、マルチスレッドと下位レベルで相互排除を実装する方法について詳しく知りたい場合は、セマフォと、これを解決するためにセマフォを実装する方法を確認することをお勧めします。

于 2015-04-08T10:09:55.853 に答える
1

EJP が指摘しているように、 のリターンはfalse多くのことを意味する可能性があり、エラーもあれば、そうでないものもあります。実際にディレクトリを作成できなかったという事実をログに記録したい場合は、後で存在を確認する必要があります。

public final class DirectoryHelper {
   private DirectoryHelper(){}

   public static boolean createDirectories(File path) {
      if (path.mkdirs()) return true; //definitely has new dir
      // if false, just look afterwards for the existence of the directory
      // also opportunity to throw own exceptions if you prefer
      return path.exists() && path.isDirectory();
   }
}

ディレクトリが後で存在しない場合にのみ false を返す新しいメソッドをここに記述しました。それが作られたばかりか、すでに存在していたかは気にしません。synchronized新しい注文のため、ブロックも必要ありません。

コードは次のようになります。

public void checkDir(String relativePath) {
    File file = new File(new File(homePath), relativePath);
    if (!file.exists()) { // just to prevent logging of existing dirs
        if (DirectoryHelper.createDirectories(file)) {
            log.info("Directory: " + file + " is created!");
        } else {
            log.error("Failed to create directory: " + file + " !");
        }
    }
};
于 2015-04-07T10:47:41.233 に答える