0

私はJava認定のために勉強しています.Mughalの本からこの例を見ています:

public class Smiley extends Thread
{
    @Override
    public void run()
    { 
        while(true)
        { 
            synchronized(this)
            {
                try
                { 
                    System.out.print(":"); 
                    Thread.sleep(100); 
                    System.out.print("-"); 
                    Thread.sleep(100); 
                    System.out.println(")"); 
                    Thread.sleep(100); 
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args)
    {
        new Smiley().start();
        new Smiley().start();
    }
}

目的は、1 行に 1 つのスマイリー :-) を出力することです。私の質問は、インスタンス (これ) で同期してもこれが達成されないのはなぜですか? なぜ静的レベルで同期する必要があるのですか?

ありがとう、

4

3 に答える 3

6

main() 関数が2 つのSmiley クラスを作成することに注意してください。そして、それらはそれぞれ独自のスレッドで実行されます。それらは でロックしているthisため、他のスレッドと競合することなく、両方ともすぐにロックを取得しようとしています。この場合、それらのロック スキームはsynchronize(this)何も達成しません。

マルチスレッドの問題に対処するときは、「何を保護しようとしているのか」を考える必要があります。 この場合、必要なSystem.out順序でアクセスしていることを確認するために、 を保護する必要があります。は静的であるため、スレッドが書き込む前に取得するSystem.out必要がある、ある種の外部スコープ ロックが必要です 。

ReentrantLockを使用してこれを実現できます。

于 2012-06-27T23:33:02.330 に答える
2

同期 (this) を使用しないでください - これは一般的に悪い習慣です。
上で説明したように - ロックは 2 つのスレッド間で共有する必要があり、この場合、ロックは各クラスのインスタンス (つまり、新しい Smiley によって作成されたオブジェクト) です。 )。
必要なのは、同じクラスのすべてのインスタンス間で共有される静的変数を使用する
か、パラメーターとしてスマイリーの CTOR にロックを渡すことによる、共有ロックです。
@Jonathon Reinhart の Reentrant Lock の使用に関する提案に基づいて、2 番目のオプションの例を示します。

public class Smiley extends Thread
{
   private  ReentantLock lock;

   public Smiley(ReentrantLock lock) { 
      this.lock = lock;
   }

   @Override
   public void run()
   { 
     while(true)
     { 
        try {
           lock.lock();
           System.out.print(":"); 
           Thread.sleep(100); 
           System.out.print("-"); 
           Thread.sleep(100); 
           System.out.println(")"); 
           Thread.sleep(100); 
        }
        catch(InterruptedException e) {
                e.printStackTrace();
        }
        finally {  
           lock.unlock();
        }
     } 
  }

}


public static void main(String[] args)
{
    ReentrantLock lock = new ReentantLock();
    new Smiley(lock).start();
    new Smiley(lock).start();
}

いくつかの指針 -
a. ロック解除コードはfinally句にある必要があることに注意してください-これは良い習慣です(catchブロックなしでtryブロックとfinallyブロックを使用することもできます).
b. 必要に応じて、 ReentrantLock を java.util.concurrent パッケージの他のロックに置き換えることを検討してください。

于 2012-06-28T05:57:21.423 に答える
1

それは実際にあなたが尋ねている2つの質問であり、答えは次のとおりです。

  • インスタンス (this) で同期してもこれが達成されないのはなぜですか?

2 つの異なる暗黙的ロックを取得しているため、同期ブロック内の命令は 2 つのスレッドで同時に実行でき、実際にはインターリーブされる可能性があります。

  • なぜ静的レベルで同期する必要があるのですか?

静的レベルで同期する必要はありません。スレッドによって共有されるオブジェクトの同じインスタンスで同期する必要があります。

目的を達成する最も簡単な方法は、次の方法で System.out を同期することです。

@Override
public void run() {
    while (true) {
        synchronized (System.out) {
            try {
                System.out.print(":");
                Thread.sleep(100);
                System.out.print("-");
                Thread.sleep(100);
                System.out.println(")");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
于 2013-11-21T17:53:37.183 に答える