3

私が理解しているように、synchronizedブロック内の以下のコードthisはカウンターのインスタンスです。

質問 1:以下の例では、スレッド A がブロックに到達するとsynchronized、スレッド B がブロックされてカウンターのインスタンスに対して何もできないということですか? 言い換えれば、これは、スレッドが好きなように実行し続けることができるが、どちらかが synchronizedブロックに到達すると、ブロックが終了するまで他のスレッドがクラスに対して何もしないことを意味しますか?

public class Counter {

    public void increment() {
        // Some code

        synchronized (this) {  // <---- "this" is an instance of Counter
             // Some more code
        }
    }
}

上記のコードを比較してください

public class Counter {

    List<String> listOfStrings = new ArrayList<String>();

    public void increment() {
        // Some code

        synchronized (listOfStrings) {  
             // Some code that deals with 
             //    listOfStrings 
        }
    }
}

質問 2:上記の例では、スレッド A がブロックに到達すると、スレッド B は、ブロック内のArrayList をsynchronized除いて、クラス内のすべての読み取りと書き込みを続行できます。これは正しいです?listOfStringsmutexsynchronized

質問 3:this複数のオブジェクトを変更する必要がある場合、を使用する必要があると仮定するのはさらに正しいmutexですか?

例えば:

public class Counter {

    List<String> listOfStrings = new ArrayList<String>();
    List<Integers> listOfIntegers = new ArrayList<Integers>();

    public void increment() {
        // Some code

        synchronized (this) {  
             // Some code that deals with 
             //    listOfStrings and listOfIntegers
        }
    }
}

私は物事を正しく理解していますか?何か言い間違いがあれば訂正してください。

4

5 に答える 5

5

スレッド B は、カウンターのインスタンスに対して何もできないようにブロックされていますか?

いいえ、スレッド B は同期されたコード ブロックに入ることがブロックされています。他のメソッド (同期されていないメソッドと別のオブジェクトを使用して同期されているメソッド) に入ることができます。スレッド B は、別のスレッドによって既に取得されているオブジェクトを使用して同期されたブロックにしかア​​クセスできません(同期ロックは再入可能です)。

スレッド B は、以下を除いて、クラス内のあらゆるものを読み書きし続けることができます。listOfStrings

listOfStrings実際には、あるブロックでミューテックスとして使用されているという事実は、synchronized他のスレッドがそのオブジェクトに明示的にアクセスできないという意味ではありません。これは、他のスレッドが同じオブジェクトによって保護された同期ブロックにアクセスできないことを意味するだけです。したがって、オブジェクトへのアクセスを保護する場合はlistOfStrings、そのオブジェクトにアクセスするすべてのメソッドを同期し、同じロックを使用する必要があります (例: listOfStrings)。

ところで、同期するすべてのオブジェクトはfinal、頭痛の種を避けるためのものです。

複数のオブジェクトに変更を加える必要がある場合、これが使用すべきミューテックスであると仮定するのはさらに正しいでしょうか?

はいといいえ。次のケースを検討してください。

List<String> listOfStrings = new ArrayList<String>();
List<Integers> listOfIntegers = new ArrayList<Integers>();
Set<String> setOfStrings = new HashSet<String>();
Set<Integers> setOfIntegers = new HashSet<Integers>();

1 つのメソッドがリストのみにアクセスし、2 番目のメソッドがセットのみにアクセスする場合、安全に 2 つのロックを使用できます。1 つは最初のメソッド用、もう 1 つは 2 番目のメソッド用です。同期してthisも問題はありませんが、パフォーマンスに影響します。

private final Object listLock = new Object();
private final Object setLock = new Object();

以降:

synchronized (listLock) {  
     // Some code that deals with 
     // setOfStrings and setOfIntegers
}

//...

synchronized (setLock) {  
     // Some code that deals with 
     // setOfStrings and setOfIntegers
}
于 2012-07-07T16:20:26.497 に答える
4

クイックアンサー:

  1. ロックsynchronized元は再入可能です。つまり、それを取得したスレッドは、同じオブジェクトの他の同期ブロックに引き続き入ることができます。そのオブジェクトの同期ブロックに入ろうとする他のスレッドはブロックされます。

  2. オブジェクトの同期は、オブジェクトを変更できないという意味ではありません。注意: 同期されたオブジェクトをミューテックス以外のものと考えないでください。どのスレッドも、オブジェクトで同期しないクラスのメソッドに入ることができます。そのメソッドがオブジェクトを変更した場合、それを防ぐことはできません。必要なものを取得するには、同期されたオブジェクト自体のクラスをスレッドセーフにする必要があります。

  3. あなたは正しくありません: あなた正しいですが、やり過ぎです。必要なクラスを同期する最大のスコープを使用することにジャンプしないでください。this実際、一般的に依存するべきではありません。内部オブジェクト ( のような「ダミー オブジェクト」であってもnew Object()) をクラスにロックする方がむしろ望ましいです。そうしないと、そのクラスのオブジェクトを使用するすべてのコードがそれらのオブジェクトで同期を試みることが許可されます。

于 2012-07-07T16:22:24.147 に答える
2

同期ブロック内のオブジェクトは単なるトークンです。つまり、トークンを保持するスレッドは実行ブロックに入ることができます (同期されている内容)。そのオブジェクトへのアクセスをロックしていません。

于 2012-07-07T16:18:21.913 に答える
1

Java では、任意のオブジェクトをミューテックスとして使用できます。java.lang.Object にはミューテックスとして機能する機能があり、wait() および notify() メソッドがあります。

同期ブロックはオブジェクトを参照します。スレッドが同期ブロックに入るとすぐに、ミューテックスとして渡されたオブジェクトがロックされます。スレッドが同期ブロックに入ろうとするたびに、mutex オブジェクトのロックをチェックします。オブジェクトがロックされている場合、スレッドはオブジェクトを待機します。作業スレッドが同期ブロックを終了すると (オブジェクトの通知メソッドが呼び出され、待機中のスレッドに通知されます)、待機中のすべてのスレッドのうち、1 つのスレッドだけがオブジェクトをロックして同期ブロックに入り、残りのスレッドはオブジェクト参照を待機し続けます。

シナリオで説明されているように、ミューテックスとして使用するオブジェクトを決定することは非常に重要です。

注意すべきもう 1 つの重要な点は、同期ブロックは複数のスレッドによるミューテックス オブジェクトの編集を妨げないということです。これは、質問 2 を参照したものです。具体的には次の文です。

上記の例では、スレッド A が同期ブロックに到達すると、スレッド B は、同期ブロックのミューテックスである listOfStrings ArrayList を除いて、クラス内のすべての読み取りと書き込みを続行できます。

スレッド B はミューテックスとして使用されているため、listOfStrings を読み書きできないという仮定は間違っています。次のシナリオでは、listOfString が複数のスレッドによって処理される可能性があります。

public class Counter {      

List<String> listOfStrings = new ArrayList<String>();  

public void decrement(){
   listOfStrings = new ArrayList<String>();
}

public void increment() {      
    // Some code      

    synchronized (listOfStrings) {        
         // Some code that deals with       
         //    listOfStrings       
    }      
 }      
}      

上記のシナリオでは、スレッド A がインクリメントを呼び出す可能性があります - 同期ブロック ロック listOfStrings に入り、同時にスレッド B がデクリメントを呼び出す可能性があります。インクリメントでミューテックスが使用されている場合でも、listOfStrings を更新できます。これは、同期ブロックがミューテックス オブジェクトの更新を妨げないためです。2 つのスレッドが同じミューテックス オブジェクトで同期ブロックに入らないようにするだけです。ミューテックスという名前は、相互排除というこの本質を強調しています。これは、同じミューテックスを使用するための異なるコード ブロックによる妥協ではなく、相互理解です。

于 2012-07-07T16:46:17.623 に答える
0

質問 1 について: 複数のスレッドが同期ブロックの外側で同時にコードを実行できますが、一度に 1 つのスレッドが同期ブロック内でコードを実行できます。一般に、クラスのインスタンス変数を変更するコードはすべてCounterブロック内に配置する必要がありますが、現在のメソッドのローカル変数でのみ機能するコードは、同期ブロックの外に出すことができます。

質問 2 と 3 について: のインスタンス変数にアクセスするすべてのコードが同期ブロック内にある限り、任意のオブジェクトで同期できますCounter(書き込み中にカウンターからデータを読み取る必要がないことを前提としています)。操作は飛行中です)。実際には、リストの 1 つ、またはObjectこの場合は明示的なプライベート スタンドアロン インスタンスで同期することをお勧めします。このクラスの外部のコードが同期モニターを直接取得する必要がない場合は、通常、クラス内部のプライベート オブジェクト。

于 2012-07-07T16:25:42.377 に答える