4

mkdir()このプログラムは、失敗するシナリオをテストするために作成しました。なぜ失敗するのですか?

うまく動作することもあれば、次のようになることもあります。

DIR :: myDir4 を作成できません DIR :: myDir4 を作成できません

そして最後に、すべてのディレクトリが作成されていることがわかりました...

すべてのテストで、作成されたすべてのディレクトリを削除します。

私のプロジェクトには、このようなディレクトリをテストして作成しようとするスレッドが100あるため、これを試しました...そして同じように失敗します...

public class DFS {
    static long time1 = System.currentTimeMillis();
    public static void main(String a[]) {
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
        new Thread(new CreteDir()).start();
    }
}

class CreteDir implements Runnable {
    public void run() {
        //Object obj = new Object();
        synchronized (this) {
        if(System.currentTimeMillis() - DFS.time1 > 10) {
            try {
                this.wait();
            }
            catch(InterruptedException ie) {
                ie.printStackTrace();
            }
        }
        File f1 = new File("myDir1");
        File f2 = new File("myDir2");
        File f3 = new File("myDir3");
        File f4 = new File("myDir4");
        File f5 = new File("myDir5");

        if (!f1.exists()&&!f1.mkdir()) {
            System.out.println("Cannot create DIR :: "+f1.getName());
        }
        if (!f2.exists()&&!f2.mkdir()) {
            System.out.println("Cannot create DIR :: "+f2.getName());
        }
        if (!f3.exists()&&!f3.mkdir()) {
            System.out.println("Cannot create DIR :: "+f3.getName());
        }
        if (!f4.exists()&&!f4.mkdir()) {
            System.out.println("Cannot create DIR :: "+f4.getName());
        }
        if (!f5.exists()&&!f5.mkdir()) {
            System.out.println("Cannot create DIR :: "+f5.getName());
        }
        this.notifyAll();
        }
    }
}
4

2 に答える 2

11

競合状態があります。

各スレッドは各ディレクトリをチェックし、まだ存在しない場合は作成しようとします。何が起こっているかは次のようなものです。

  • スレッドAのテストmyDir4で存在しないことが判明
  • スレッドBのテストmyDir4で存在しないことが判明
  • スレッドAが作成されmyDir4ます ... 成功!
  • スレッドBが作成されmyDir4ます ... 失敗! すでに存在します。

これは、どのディレクトリでも発生する可能性があります...またはまったく発生しない可能性があります.OSがJavaスレッドをスケジュールする方法などによって異なります。


あなたのコードは で同期しようとしていますthisが、その試みは効果がありません。は、現在のスレッドが使用してthisいる のインスタンスになりますCreteDirが、各スレッドは異なるインスタンスを持つため、実質的にスレッド間同期はありません。効果的に同期するには、すべてのスレッドが同じオブジェクトで同期する必要があります...しかし、粒度が間違っているため、マルチスレッドが無効になります。

実際、マルチスレッド戦略全体を再考する必要があります。これが「実際のコード」ではないということは、その方法についてアドバイスすることはできないということです。

最後のコメントの行間を読むと、3 つの戦略が考えられると思います。

  • ディレクトリが存在しない場合は、「グローバル」ロックを使用してディレクトリの作成を同期するだけです。このようなもの:

    // Test first without locking to reduce the concurrency bottleneck
    if (!dir.exists()) {
        synchronize (globalDirLock) {
            // Repeat the test while holding the lock
            if (!dir.exists()) { 
                if (!dir.mkdir()) {
                    System.out.println("OOOPS!");
                }
            }
        }
    }
    
  • ディレクトリごとに 1 つの (ロック) オブジェクトを持つメモリ内データ構造を作成します。2 つの同時クライアント要求が 1 つのディレクトリに対して 2 つのロック オブジェクトを作成するという競合状態を回避するために、そのデータ構造への入力は慎重に行う必要があります。

    (このスキームを少し調整すると、おそらくロック オブジェクトの存在を使用して、ディレクトリが存在することを繰り返しチェックすることを回避できます。チェックにはシステムコールが含まれ、プロセッサのオーバーヘッドの点で自明ではありません。)

  • File.mkdir()returnの場合は無視してfalseください。(たぶん、別のことをするFile.exists()か、File.isDirectory()テストしてください...念のため。)

于 2012-10-07T10:49:30.807 に答える
0

グローバルロックを追加する代わりに、これを試すことができます:

if (!f1.exists()&&!f1.mkdir()&&!f1.isDirectory()) {
            System.out.println("Cannot create DIR :: "+f1.getName());
        }

isDirectory()このディレクトリがすでに作成されているかどうかがわかります。

于 2016-06-01T13:58:25.807 に答える