5
public class ObjectCounter {
    private static long numOfInstances = 0;
    public ObjectCounter(){
        synchronized(this){
        numOfInstances++;
        }
    }
    **public static synchronized long getCount(){
        return numOfInstances;
    }**
//vs//
    **public static long getCount(){
        return numOfInstances;
    }**
}

いくつかのスレッドを実行すると、そのうちのいくつかは静的関数getCount()を呼び出し、いくつかは新しいインスタンスを作成します。getCount()その時点で実際のインスタンス数を呼び出すたびに取得したいと考えています。

  1. コード内の 2 つのオプションに違いはありますか?
  2. " " をロックした場合、コンストラクターが同期ブロックを終了するまでthis呼び出すことができないということではありgetCount()ません (getCount() で同期を記述しない場合としましょう)。
  3. コード内のどこかで同期ブロックを実行すると、同期ブロックのみがロックされますか?それともすべての " this" コードがロックされますか?
  4. ここから編集: ありがとうございました。とても役に立ちましたが、回答に続いていくつか質問があります。
  5. 私が正しく理解していれば、同期された(this)ブロックは静的同期関数に影響を与えません(または接続されません)(numOfInstancesインクリメントではなくロック条件で)?
  6. インクリメントと getCount() 関数をスレッドセーフにするためのより良いオプションはありますか? (静的オブジェクトを開いて、synced(this) の代わりに synchronized(obj) を実行するように - 友人が提案)。
  7. ObjectCounter クラスに f1() メソッド (非静的) がある場合、1 つのスレッドが同期 (this) にあるときに、他のスレッドは f1() ブロック (同期クラスではないか、内部に同期ブロックがある) に入ることができますか?
  8. ObjectCounter に f1() メソッド (非静的) と f2() メソッド (非静的) がある場合、f1() に同期 (this) ブロックがあります。1 つのスレッドが synchronized(this) ブロックにある間、他のスレッドは f1() ブロックに入ることができますか (同期クラスではないか、内部に同期ブロックがあります)? (両方のスレッドが同じインスタンスで「動作」しているとしましょう)

`

4

4 に答える 4

9

synchronizedスレッドがそのブロックまたはメソッドを実行するための手段を使用して、そのブロックまたはメソッドによって (明示的または暗黙的に) 参照されるロックを取得する必要があります。メソッドの場合static synchronized、そのロックはクラス オブジェクトのモニターです。ブロックの場合synchronized(this)、使用されるロックは現在のインスタンスのモニターです。複数のメソッドまたはブロック間でロックを共有すると、更新の原子性とメモリの可視性が強化されます。また、共有ロックは、待機と通知を行うための共有通信パスを提供します。

静的同期ブロックはコンストラクターのブロックで使用されるロックとは異なるロックを使用するため、静的同期ブロックに入ることは、別のスレッドが現在のインスタンスでロックを取得する必要があるブロックとコンストラクターの同期ブロックにアクセスすることによってブロックされません。ロックの取得は常に競合しません。ここでさらに重要なことは、コンストラクター内の 1 つのスレッドによって行われた変更が、getter を使用する他のスレッドに表示されない可能性があることです。同期は、ロックとメモリの可視性の両方に影響します。

この変更されたバージョンは機能します:

public class ObjectCounter {
    private static long numOfInstances = 0;
    public ObjectCounter(){
        synchronized(ObjectCounter.class){
            numOfInstances++;
        }
    }
    public static synchronized long getCount(){
        return numOfInstances;
    }
}

getter とインクリメント ブロックが同じロックを使用しているためです。異なるスレッドが同じモニターを取得するようにすると、ゲッターにアクセスする別のスレッドが更新された値を確認できるように、カウンターへの変更が安全に発行されます。

同期されたキーワードは、「入る前にロックを取得する必要がある」と述べています。メソッドの場合、ロックが想定されます。メソッドに static キーワードがあると、クラスのモニターになり、 static キーワードがないと、クラスのモニターになります現在のインスタンス。ロックが正しく機能するには、異なるブロックとメソッドが同じロックを使用する必要があります。Java の設計方法には、間違いなく構文シュガーが多すぎて便利になりすぎています。ロックの暗黙的な選択を許可し、モニターを java.lang.Object に配置すると、混乱が生じる可能性があります。

あなたの質問 #6 を WRT してください: ここで行っていることについては、AtomicLongを使用したほうがよいでしょう。同期ブロックを使用して、他のスレッドからの干渉なしに行う必要がある複数の変更を調整します。

質問 #3、#7、および #8 は非常に似ているように見えます。メソッド/ブロックがロックを取得しようとしていない場合、スレッドがそのメソッド/ブロックを実行するのを妨げるものは何もありません。オブジェクト全体は保護されません。同期されたメソッドまたはブロックを使用してロックを強制することが、保護を行うものです。「キーワードを使用する」という観点ではなく、synchronizedスレッドが何をロックする必要があるかという観点から考えてください。

于 2015-07-10T13:56:40.280 に答える
2
  1. はい、オプションに違いがあります。getCount()上記のオプションでは、2 つのスレッドを同時に呼び出すことはできませんが、下のオプションでは可能です。

  2. はい、そうです。オブジェクトのロックを同時に保持できるスレッドは 1 つだけです。

  3. 各オブジェクトには独自のロックがあります。したがってsynchronized (this)、そのオブジェクトのすべてのブロックをロックします。

ただし、各オブジェクトには独自のロックがあり、各クラスにも独自のロックがあることに注意してください。コンストラクターでは、オブジェクト ロックを使用して静的 (クラス) 変数にアクセスしますが、コンストラクターでgetCount()はクラス ロックを使用します。つまり、あなたのコードはスレッドセーフではありません!

于 2015-07-10T13:59:08.297 に答える
0

synchronized手順:

  1. オブジェクト ロックが既に取得されているかどうかを確認します。その場合は、同期ブロック/メソッドに進みます
  2. ロックの取得を試みます。ロックが別のスレッドによってすでに取得されている場合、スレッドはロックが解放されるまで待機し、その時点でサイクル (2.) を再度実行します。
于 2015-07-10T14:00:19.550 に答える
-2

コード内の 2 つのオプションに違いはありますか?

はい、明確な違いがあります。getCount()最初に、 のクラス オブジェクトのメソッドへのスレッド アクセスを同期していますObjectCounter。2番目にいる間はそうではありません。

「これ」をロックした場合、請負業者が同期ブロックを終了するまで getCount() を呼び出すことができないことを意味するべきではありません (getCount() で同期を記述しない場合としましょう)。

オブジェクトのロックは 1 つしかないため (クラス ロックは異なり、staticキーワードを とともに使用して保持されますsynchronized)、他のスレッドがそのロックを取得している場合、 synchronized(this){またはこれが原因でsynchronized long getCount(){、ロックを取得しようとしている新しいスレッドは次のようになります。前のスレッドがロックを解放するまで待機します。

今あなたの場合、あなたはやっているstatic synchronized long getCount(){ので、そのロックは とは異なりsynchronized(this){ます。つまり、何らかの理由でスレッドがロックを取得してsynchronized(this){おり、他のスレッドが呼び出そうとしてgetCount()いる場合、そのスレッドはブロックされません。

コードのどこかで同期ブロックを実行すると、同期ブロックのみがロックされますか、それともすべての「この」コードがロックされますか?

  1. 非静的同期: コード内のどこかで同期ブロックを実行し、それが非静的public synchronized long getCount(){である場合、オブジェクトのロックも保持されるため、ロックを取得しようとする新しいスレッドは、前のスレッドが完了するまで待機する必要があります。ロックを解除しました。

  2. 静的同期: コード内のどこかで同期ブロックを実行し、それが staticpublic static synchronized long getCount(){である場合、非静的同期のロックには影響しません。


結論:

  • 1つのオブジェクトに対して 1 つのロックしかなく、そのロックがスレッドによって取得された場合、他のスレッドはそのロックが解放されるまで待機する必要があります。

  • static次に、 keyword が keyword と一緒に使用された場合に保持されるクラス ロックがありsynchronizedます。

于 2015-07-10T13:57:44.493 に答える