1

以下のプログラムが 12 ではなく 11 を出力するのはなぜですか? スレッドは同じインスタンス変数を使用していませんか? 説明してください?

public class Tester extends Thread {

private int i;

public static void main(String[] args){
 Tester t = new Tester();
 t.run();
 System.out.print(t.i);
 t.start();
 System.out.print(t.i);

}

public void run(){ i++;}

}

上記のコードは正常にコンパイルされます。i は、オブジェクトの構築時にデフォルトで 0 値に設定されます。関係概念の前に発生します。スレッドの開始前に実行されたすべてのコードが完了します。概念は、インスタンス変数は複数のスレッドで共有されます。ここでは、メイン スレッドとテスター スレッドの 2 つのスレッドが実行されます。だから私は両方のスレッドと共有する必要がありますか? - i が共有されていて、テスター スレッドを開始する前に事前発生関係が維持されている場合、インクリメントされた i の値はテスター スレッドに表示されますか?

4

5 に答える 5

3

新しいスレッドに変数を増やす時間を与えて、試してみてください

public static void main(String[] args){
  Tester t = new Tester();
  t.run();
  System.out.println(t.i);
  t.start();
  try {
    Thread.sleep(1000); // 1 sec
  } catch (Exception ex) {}
  System.out.println(t.i);
}

コードの唯一の問題は、t.i値を出力してから待機しないt.start()ことです。スレッドが値を増やす前に値を出力します。

于 2012-06-26T04:14:25.790 に答える
2

インスタンス変数は、複数のスレッドを公開するなどの方法でアクセスできるようにすると、複数のスレッドからアクセスできます。

コードでは次のようになります。Testerスレッドtはそれ自身の変数にアクセスし、メイン スレッドからも同じ変数にアクセスします。Testet値を出力するように要求すると、その時点でのスレッドの任意の値が出力される場合がありますt

メイン スレッドがメソッドを呼び出すと、メイン スレッドrunで実行され、実質的にフィールドの値が 1 に増加します (デフォルトは 0 です)。その後、メソッドを呼び出すとstart、別のスレッドが開始され、Java VM がメソッドを呼び出し、run再びフィールドが 1 から 2 にインクリメントされます。

したがって、出力は1であり、メインスレッドから値を出力するように要求する前にスレッドが実行する時間があったかどうかに応じて、1または2の可能性があると予想されます....得られる正確な結果は異なりますあなたのマシンでは、別のコンピューターでは別の話になる可能性があります。これは、CPU、オペレーティング システム、使用可能なメモリなどによって異なります。

dash1eが彼の回答で示唆したように、スレッドがバックグラウンドで実行されているThread.sleep(1000);間、メイン スレッドを待機させるために使用できます。これにより、メインスレッドがフィールドの値を印刷するように要求する前に、スレッドがフィールドの値を更新する可能性が大幅に高まります。とはいえ、タスクが完了するのを待つために使用するだけでは十分ではないことを明確にしたいと思います...必要に応じて、特定の基準が満たされているかどうかを確認する while の中に呼び出しを入れることができます、それはスピニングとして知られています。TestertTestertThread.sleep(1000);Thread.sleep

メインスレッドからフィールドの値を出力できるという事実は、別のスレッドからアクセスできることを示しています。スレッドが通信したいので、それは良いことです...どういうわけか。

int のみなのでアクセスしても問題なく、int が無効な状態になることはないため、アクセスを同期する必要はありません。最新ではない値を取得する可能性がありますが、バックグラウンドで変更されるため、あまり信頼できません。

単一のスレッドのみがアクセスでき、スレッドごとに独立して存在する値が必要な場合は、ThreadLocalを参照してください。

于 2012-06-26T04:08:54.117 に答える
0

スレッドを開始しましたが、停止するのを待っていません。t.join()仕上がり待ちにご利用ください。そして、はい、スレッド同期の問題がありますが、それは別の問題です。

public class Tester extends Thread {

    private int i;

    public static void main(String[] args) throws InterruptedException {
        Tester t = new Tester();
        t.run();
        System.out.println(t.i);
        t.start();
        t.join();
        System.out.println(t.i);
    }

    public void run() {
        i++;
    }

}
于 2012-06-26T04:41:23.213 に答える
0

私が探していた主な答えは、事前発生の観点からでした: 以下に詳述する概念:

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

したがって、ドキュメントには、a)スレッドが開始する前に、それより前のすべてのステートメントが完了する、b)スレッド実行内のすべてのステートメントが新しいスレッドの終了時に完了すると書かれています。

上記の理解に沿って進みます-開始された新しいスレッドに常に表示されている必要があります。そして、これは常に、すべてのシステムでそうであるべきです。そのため、start が呼び出されて新しいスレッドが起動されると、i は常に 1 と見なされます。

しかし、i 値を出力している時間は、テスター スレッドではなく、メイン スレッドによって実行されています。そのため、新しいテスター スレッドが値を 1 として認識し、それを 2 としてインクリメントする場合でも、i++ はアトミック操作ではないため、メインでの実行はそれを反映しません。

ここで、int を次のようにしようとするとします。

 private volatile int i;

volatile は、特定の変数だけでなく、それまでのステートメントについても事前発生関係を保証します。

実行されるメイン スレッドの println は、インクリメントが始まる前に実行される場合があります。そのため、変数を volatile にした後でも、11 が表示されることがあります。変数を AtomicInteger にする場合も同様です。

run メソッドが呼び出されると、増分された値が表示されます。

System.out.println("i "+  i.incrementAndGet()); 

しかし、メインスレッドではありません。run メソッドと main メソッドでデータの可視性が異なります。使用されるインスタンス変数は、実行中の両方のスレッドで同じです。

于 2012-06-26T04:32:47.310 に答える
0

私はあなたのコード:

public class Tester extends Thread {

private int i;

public static void main(String[] args){
 Tester t = new Tester();
 t.run();
 System.out.print(t.i);
 t.start();
 System.out.print(t.i);

}

public void run(){ i++;}

}

t.run() と t.start() を呼び出します。実行中のt.run()スレッドとt.start()スレッドの 2 つのスレッドがあります。

そのi変数では、同期していない 2 つのスレッド間で共有します。

そのため、i変数の値がスレッド間で更新されないことがあります。volatile キーワードを使用して同期できます

private volatile int i;

またはコードセグメントを同期すると値が増加しますi

public void run(){ 
    synchronized(this){
        i++;
    }
}
于 2012-06-26T10:52:45.517 に答える