7

Java プログラムのメイン メソッドで while ループを実行しています。ループは、ブール値フラグ変数がプログラムの keyPressed メソッドで true に設定されるまで実行されることになっています (プログラムを KeyListener として JFrame に追加しました)。

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;

public class ThreadWhile implements KeyListener {
    private boolean flag = false;

    public static void main(String[] args) {
        //Set up key listening on a dummy JFrame
        JFrame frame = new JFrame("Key Detector 9000");
        frame.setVisible(true);
        ThreadWhile tw = new ThreadWhile();
        frame.addKeyListener(tw);

        System.out.println("Looping until flag becomes true...");
        while(!tw.flag) {
            //Commenting the println out makes the loop run forever!
            System.out.println(tw.flag); 
        }
        System.out.println("Flag is true, so loop terminated.");
        System.exit(0);
    }

    public void keyPressed(KeyEvent e) {
        flag = true;
        System.out.println("flag: " + flag);
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}
}

keyPressed メソッドは独自のスレッドで実行されることを理解しているため、キーを押すと、変数 'flag' を true に設定し、main メソッドで実行されている while ループを終了する必要があるようです。

ただし、このプログラムを実行すると、'flag' 変数が正しく true に設定されていることがわかりますが、ループは永久に実行されます! 奇妙なことに、while ループ内に「flag」変数の System.out.println エコーをすばやく挿入すると、プログラムは正しく動作しますが、明らかにループ内で何も出力したくありません。

この問題は、Java コンパイラが「フラグ」変数のチェックを実際に停止するポイントまで空の while ループを最適化しようとした結果である可能性があると推測しています。これを正しく機能させるための提案、または keyPressed スレッドが実行されるまでメインスレッドを一時停止させるためのより気の利いた同時実行ベースのアプローチを誰かが提案していますか?

ありがとう!

4

3 に答える 3

9

フラグを宣言する必要がありますvolatile。そうしないと、コンパイラがコードを最適化し、フラグの読み取りをスキップできます。

于 2012-07-21T21:14:15.933 に答える
4

他の人が提案したvolatileソリューションは機能するはずですが、whileループ内のコードを継続的に実行する必要がない限り、代わりに同期セクション内でwait()and notify()(またはnotifyAll())を使用する必要があります(「ビジーウェイト」を回避するため)。何かのようなもの:

public class ThreadWhile implements KeyListener {
    private boolean flag = false;
    private Object flagLock = new Object();

    public static void main(String[] args) {
        //Set up key listening on a dummy JFrame
        JFrame frame = new JFrame("Key Detector 9000");
        frame.setVisible(true);
        ThreadWhile tw = new ThreadWhile();
        frame.addKeyListener(tw);

        System.out.println("Waiting until flag becomes true...");
        synchronized (tw.flagLock) {
            while(!tw.flag)
                tw.flagLock.wait();   // Note: this suspends the thread until notification, so no "busy waiting"
        }
        System.out.println("Flag is true, so loop terminated.");
        System.exit(0);
    }

    public void keyPressed(KeyEvent e) {
        synchronized (flagLock) {
            flag = true;
            System.out.println("flag: " + flag);
            flagLock.notifyAll();
        }
    }

    ...

そうしないと、メインスレッドでサイクルを繰り返し浪費して、何度も何度も値をチェックしますflag(これは、一般に、十分に発生した場合、他のスレッドのCPUの速度を低下させ、モバイルデバイスのバッテリーを消耗させる可能性があります)。これはまさにそのwait()ために設計された種類の状況です。

于 2012-07-21T21:14:46.697 に答える
0

1.フラグにキーワードを使用する必要があります。volatileインスタンス変数で volatile を呼び出すと、JVM にアクセスするスレッドがこのインスタンス変数の独自のコピーと、メモリ

2.揮発性は、スレッド内の値のキャッシュを防ぐのにも役立ちます。

3. Volatile は、その変数が 1 つのスレッドで変更された値を別のスレッドに反映するようにしますが、競合状態を防ぐことはできません

4.したがって、フラグの値を設定する Atomic ステートメントで synchronized キーワードを使用します。

例えば:

public void keyPressed(KeyEvent e) {
     synchronized(this){
        flag = true;
        System.out.println("flag: " + flag);
      }
    }
于 2012-07-21T21:32:44.150 に答える