Javaのスレッドは一見条件文をスキップ

アダム・ジャービス:

私が書いている最近のライブラリーのために、私は無限にループするスレッドを書きました。このループでは、私は、ねじ付きオブジェクトのプロパティをチェックする条件文で始まります。それは初期どのような値プロパティが持っているようだが、それも更新された後に返す何になります。

ない限り、私は中断のいくつかの種類を行うなど、Thread.sleepまたはprint文。

私は本当に残念な質問をするかどうかはわかりません。そうでなければ私は、Javaのドキュメントで見ていることになります。私は簡単な言葉で問題を説明し、最小限の例にコードを煮詰めてきました。

public class App {
    public static void main(String[] args) {
        App app = new App();
    }

    class Test implements Runnable {

        public boolean flag = false;

        public void run() {
            while(true) {

                // try {
                //     Thread.sleep(1);
                // } catch (InterruptedException e) {}

                if (this.flag) {
                    System.out.println("True");
                }
            }
        }

    }

    public App() {
        Test t = new Test();
        Thread thread = new Thread(t);
        System.out.println("Starting thread");
        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}

        t.flag = true;
        System.out.println("New flag value: " + t.flag);
    }
}

今、私たちはの値を変更した後にすることを前提としますflag実行中のスレッドのプロパティを、私たちはすぐに「真」は、端末への吐き出しの塊を見ることになります。しかし、我々にはありません..

もしIアンコメントThread.sleep期待通りに糸ループ内の行は、プログラムが動作し、我々はの値を変更した後、「TRUE」が印刷されているの多くの行を参照Appオブジェクトを。さらにとして、の代わりに任意の印刷方法はThread.sleepまた、動作しますが、いくつかの簡単な代入コードにはありません。私はそれがコンパイル時に未使用のコードとして引き出されているためであると仮定します。

だから、私の質問は本当にです:なぜ私が正しく状況をチェックするスレッドを取得するために中断のいくつかの種類を使用する必要がありますか?

スティーブンC:

だから、私の質問は本当にです:なぜ私が正しく状況をチェックするスレッドを取得するために中断のいくつかの種類を使用する必要がありますか?

さて、あなたはしないでくださいする必要があります「中断」を使用せずに、この特定の例を実装するために、少なくとも2つの方法があります。

  • あなたが宣言した場合flagであることをvolatile、それが動作します。
  • それはあなたも仕事を宣言するかどうかflagであることをprivate、書き込みsynchronizedgetterメソッドとsetterメソッド、およびすべてのアクセスのためにそれらを使用します。

    public class App {
        public static void main(String[] args) {
            App app = new App();
        }
    
        class Test implements Runnable {
            private boolean flag = false;
    
            public synchronized boolean getFlag() {
                return this.flag;
            }
    
            public synchronized void setFlag(boolean flag) {
                return this.flag = flag;
            }
    
            public void run() {
                while(true) {
                    if (this.getFlag()) {   // Must use the getter here too!
                        System.out.println("True");
                    }
                }
            }
        }
    
        public App() {
            Test t = new Test();
            Thread thread = new Thread(t);
            System.out.println("Starting thread");
            thread.start();
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
    
            t.setFlag(true);
            System.out.println("New flag value: " + t.getFlag());
    }
    

しかし、なぜあなたはこれを実行する必要がありますか?

あなたがいずれかを使用しない限り、そのためvolatilesynchronized(および使用synchronized後、1つのスレッドがメモリを参照することは保証されませんが正しく)他のスレッドによって行われた変更。

あなたの例では、子スレッドはの最新の値が表示されませんflag(これは条件自体が間違っているか、「仕事をしない」ということではありません。彼らは実際に古い入力を取得している。これは、「ゴミ出しゴミ、で」です。)

Java言語仕様は、正確に1つのスレッドが(以前)を参照することが保証される条件が別のスレッドによって行われた書き込み設定しています。スペックのこの部分は、Javaメモリモデルと呼ばれ、それが中ですJLS 17.4で説明理解し、より簡単にあり実践でのJava並行処理をブライアン・ゲッツらによっては。

予期しない動作がレジスタにフラグを維持することを決定するJITが原因である可能性があることに注意してください。また、JITコンパイラは、それが力のメモリキャッシュのライトスルー、エトセトラを必要としないことを決定したことが考えられます。(JITコンパイラは、すべてのフィールドへのすべてのメモリライトにライトスルー強制する必要はありません。それは最も近代的なマシンがあり、マルチコアシステム...上の主要なパフォーマンスヒットになります。)


Javaの中断メカニズムはまだこれに対処する別の方法です。方法はそれを呼び出すので、あなたは、任意の同期を必要としません。あなたが割り込みにしようとしているスレッドが現在待機中または中断操作でブロックされたときに加えて、中断が動作します。で例えばObject::waitコール。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=332336&siteId=1
おすすめ