1

次のリンクを参照してください http://docs.oracle.com/javase/tutorial/essential/concurrency/QandE/answers.html次 の例は、キーステートメント1がキーステートメント2の前に実行されることが保証されていないことを示しています。

public class BadThreads {

    static String message;

    private static class CorrectorThread
        extends Thread {

        public void run() {
            try {
                sleep(1000); 
            } catch (InterruptedException e) {}
            // Key statement 1:
            message = "Mares do eat oats."; 
        }
    }

    public static void main(String args[])
        throws InterruptedException {

        (new CorrectorThread()).start();
        message = "Mares do not eat oats.";
        Thread.sleep(2000);
        // Key statement 2:
        System.out.println(message);
    }
}

ソリューションには、メッセージへのすべての変更がメインスレッドに表示されることを保証できる2つの方法があります。

  • メインスレッドで、CorrectorThreadインスタンスへの参照を保持します。次に、メッセージを参照する前に、そのインスタンスでjoinを呼び出します
  • 同期されたメソッドを使用してメッセージをオブジェクトにカプセル化します。これらの方法を除いて、メッセージを参照しないでください。

これらの手法は両方とも、必要な発生前の関係を確立し、メッセージへの変更を可視化します。

joinを使用する最初のソリューションがキーステートメント1を「happen-before」キーステートメント2にする方法を理解しています。しかし、2番目のソリューションでは、同期メソッド(getMessage()やsetMessage()など)を使用してこの関係を確立する方法を理解できません。修飾キーステートメント2(System.out.println(getMessage())が修飾キーステートメント1(setMessage( "Mares do eat oats"))の後に実行されるという保証は何ですか。キーステートメント2はロックを取得する可能性がありますスレッドのスケジュール方法に応じて、キーステートメント1の前のメッセージまたはその逆。

また、message = "Mares do eatoats"の前にmessage="Mares do not eat oats"を実行するようにコードを変更する方法はありますか?私が考えることができる1つの方法は、共有状態変数を保持し、message = "Mares do eat oats"が実行された後に設定することですが、message = "Mares do eat oats"はwhile(!stateVariable){のように保護されたブロックにある必要があります。 wait();}そしてメッセージを更新します。そうですか?

ありがとう。

4

3 に答える 3

3

発生前のアクションと特定のアクションの順序を混同しないでください。その関係のポイントは、それが特定の順序ではなく、そのような順序がまったく存在するということです。あなたの例では、順序付けはありません。より多くのsleepステートメントを使用して、他のスレッドが書き込んだ内容を読み取ろうとしても、一方のスレッドが他方のスレッドのアクションを監視しない場合があります。

wait2つのアクションを調整したい場合は、 /に頼らないでください。notifyこれらは非常に低レベルで壊れやすいメカニズムです。java.util.concurrentたとえば、から何かを使用しCountDownLatchます。

于 2013-03-10T19:18:57.970 に答える
3

発生前のことは、あるステートメントが他のステートメントの前に実行されることを保証するものではありません。これは、最初のステートメントが2番目のステートメントの前に実行された場合に保証されます(両方のスレッドが同時に開始され、一方が1秒間スリープし、もう一方が2秒間スリープするため、サンプルで発生する可能性が高くなります) 2番目のものは最初のものが書いたものを見るでしょう。

変数への同期アクセスにより、この変数への書き込みが、別のスレッドからのこの変数の後続の読み取りに表示されることが保証されます。なんで?これは、Javaメモリモデルの仕様が示していることであり、実装が仕様を尊重しているためです。

保証する方法は他にもあります。変数を揮発性にするか、AtomicReferenceにするか、変数を格納して並行コレクションから取得します。

于 2013-03-10T19:24:01.967 に答える
2

そのサイトで説明されている回答では、それは次のように言っています:

ただし、「キーステートメント1」と「キーステートメント2」の間に発生前の関係がないため、この結果は保証されません。これは、「キーステートメント1」が実際に「キーステートメント2」の前に実行された場合でも当てはまります。覚えておいてください。発生前の関係は、順序ではなく可視性に関するものです。

それからそれは言う:

メッセージへのすべての変更が メインスレッドに表示されることを保証できる方法は2つあります。

Mares do eat oats.きっと結果として印刷されるとは言えません。
そして定義により、可視性と原子性Synchronizationの両方を保証します。したがって、メッセージが設定され、オブジェクトの同期メソッドを介して取得される場合、メッセージがメソッドによって変更された場合、この変更された値は、メッセージを読み戻すときにメソッドによって確実に表示されます。したがって、コードをこれに変更すると、次のようになります。setget

class Message
{
    String message ;
    public Message(){}
    public Message(String message)
    {
        this.message = message;
    }
    public synchronized void setMessage(String message)
    {
        this.message = message;
    }
    public synchronized String getMessage()
    {
        return message;
    }
}
public class BadThreads {

    //static String message;
    static Message mess = new Message();
    private static class CorrectorThread extends Thread 
    {
        public void run() 
        {
            try 
            {
                sleep(1000); 
            } catch (InterruptedException e) {}
            // Key statement 1:
            mess.setMessage ("Mares do eat oats."); 
        }
    }

    public static void main(String args[]) throws InterruptedException 
    {
        (new CorrectorThread()).start();
        mess.setMessage("Mares do not eat oats.");
        Thread.sleep(2000);
        // Key statement 2:
        System.out.println(mess.getMessage());
    }
}

そして、それKey Statement 1以前に実行された場合、メッセージの最新の変更された値がメインスレッドkey statement 2を介して取得されることが保証されます。getMessage()

于 2013-03-10T20:32:49.410 に答える