1

Javaスレッドを使用したこのコードがあります:

public class ThreadExample implements Runnable{

    public Thread thread;
    static int i = 0;
    ThreadExample(){
        thread = new Thread(this);
        thread.start();
    }

    public static void main(String[] args) {
        ThreadExample example = new ThreadExample();
        for(int n =0; n<1000; n++){
            System.out.println("main thread "+i);
            i++;
        }
    }

    public void run(){
        for(int index=0; index<1000; index++){
            System.out.println("Sub thread "+i);
            i++;
        }
    }
}

実行すると、結果は次のようになります。

main thread 0
Sub thread 0
Sub thread 2
Sub thread 3
Sub thread 4
Sub thread 5
Sub thread 6
Sub thread 7
Sub thread 8
Sub thread 9
Sub thread 10
Sub thread 11
Sub thread 12
main thread 1
....

実行されるスレッドがその順序に従わないことを知っています。しかし、私が理解していないのは、変数 i が 12 になったときにメインスレッドが 1 (変数 i を出力する) を出力するのはなぜですか? (サブスレッドが 12 に出力されたため)。

ありがとう :)

4

4 に答える 4

6

ほとんどの場合、テキストの印刷準備が整ってから実際に印刷できるようになるまでに大きな遅延があることが原因です。System.outメイン スレッドはステートメント「メイン スレッド 1」を準備し、実際に出力するために、サブ スレッドがロックを解放し、ロックを取得するまで待機する必要がありました。

ivolatile に設定しても発生する場合は、ほとんどこれが唯一の説明です。

于 2012-12-22T18:03:33.400 に答える
1

volatileこれは、何らかの同期またはキーワードがないと、スレッドが互いの変更を認識しないために発生します。

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4を参照してください。

于 2012-12-22T18:36:58.153 に答える
1

この結果は、ロックの取得 (System.out.println 呼び出しの裏で行われる) が本質的に公平ではないことが原因である可能性があります。不公平な実装、または少なくとも部分的に不公平な実装は、通常、スレッドがロックを待機するのと同じ順序でロックを取得することを保証しません。

その結果、2 つのスレッドがタイトなループで繰り返しロックを競合している場合 (例のように)、通常、1 つのスレッドによる取得が実行され、次に別のスレッドによる取得が実行されます。多くの場合、不公平なロックは実装が簡単であり、一般に、競合の激しいロックの完全に公平な代替手段よりもパフォーマンス (スループット) が向上します。これは、ロック コンボイ効果の影響を受けないためです。明らかな欠点は、特定の待機中のスレッドがロックを取得する順序について保証されていないことです。理論的には、一部の待機中のスレッドは確実に枯渇する可能性があります (実際には、これは発生しない可能性があります。または、ロックが一部のスレッドが異常な時間待機している場合に公正な動作にフォールバックします)。

不公平なロックを考えると、パターンは自然です。2 番目のスレッドのループがロックを取得し、最初のスレッドが 1 を読み取って待機を開始した直後に、出力する文字列がこの時点で先頭のテキストと連結されているため、値 1 が「焼き付けられます」。メイン スレッドは、別のスレッドでいくつかのロック/ロック解除のペアを待機する必要があり、その時点で実行の機会が得られ、古い値が出力されます。

別の説明は、volatile がないため、インタープリターまたは JIT が i の共有値を読み取ることすらなく、変数をレジスターにホストしているというものです。 i を変更する可能性のある介在メソッド。

于 2012-12-23T03:13:26.743 に答える
0

私の知る限り、これはのバッファリングが原因ですSystem.out

System.out.flush()各 printlen の後に、ファイルへの書き込みまたは呼び出しを試みます。

i同期された方法で増やしてみてください:

public synchronized void increment() {
  i++;
}
于 2012-12-22T18:15:10.557 に答える