38

私の建築家はいつもそれを言います

ブール値で同期しない

私はその理由を理解することができません、そして誰かがそれが良い習慣ではない理由について例を挙げて説明することができれば本当にありがたいです。 参照サンプルコード

private Boolean isOn = false;
private String statusMessage = "I'm off";
public void doSomeStuffAndToggleTheThing(){

   // Do some stuff
   synchronized(isOn){
      if(isOn){
         isOn = false;
         statusMessage = "I'm off";
         // Do everything else to turn the thing off
      } else {
         isOn = true;
         statusMessage = "I'm on";
         // Do everything else to turn the thing on
      }
   }
}
4

5 に答える 5

72

「ブール値で同期しない」必要がある理由がわかりません。

常に定数オブジェクトインスタンスsynchronizeを使用する必要があります。割り当てているオブジェクトで同期した場合(つまり、オブジェクトを新しいオブジェクトに変更した場合)、それは一定ではなく、さまざまなスレッドがさまざまなオブジェクトインスタンスで同期します。それらは異なるオブジェクトインスタンスで同期しているため、複数のスレッドが同時に保護されたブロックに入り、競合状態が発生します。これは、、などで同期する場合と同じ答えです。LongInteger

// this is not final so it might reference different objects
Boolean isOn = true;
...
synchronized (isOn) {
   if (isOn) {
      // this changes the synchronized object isOn to another object
      // so another thread can then enter the synchronized with this thread
      isOn = false;

さらに悪いことに、Booleanオートボクシング()によって作成されたものは、すべてのオブジェクトのシングルトンである(または)isOn = trueと同じオブジェクトです。ロックオブジェクトは、それが使用されているクラスに対してローカルである必要があります。そうでない場合、他のクラスが同じ間違いを犯している場合に他のクラスがロックする可能性があるのと同じシングルトンオブジェクトをロックします。Boolean.TRUE.FALSEClassLoader

ブール値をロックする必要がある場合の適切なパターンは、private finalロックオブジェクトを定義することです。

private final Object lock = new Object();
...

synchronized (lock) {
   ...

または、オブジェクトの使用も検討する必要AtomicBooleanがあります。つまり、オブジェクトをまったく使用する必要がない場合がありますsynchronize

private final AtomicBoolean isOn = new AtomicBoolean(false);
...

// if it is set to false then set it to true, no synchronization needed
if (isOn.compareAndSet(false, true)) {
    statusMessage = "I'm now on";
} else {
    // it was already on
    statusMessage = "I'm already on";
}

あなたの場合、スレッドでオン/オフを切り替える必要があるように見えるのでsynchronize、オブジェクトをオンにしlockてブール値を設定し、競合状態のテスト/設定を回避する必要があります。

synchronized (lock) {
    if (isOn) {
        isOn = false;
        statusMessage = "I'm off";
        // Do everything else to turn the thing off
    } else {
        isOn = true;
        statusMessage = "I'm on";
        // Do everything else to turn the thing on
    }
}

最後に、他のスレッドからアクセスされることが予想される場合は、取得中にもアクセスしない限りstatusMessage、としてマークを付ける必要があります。volatilesynchronize

于 2012-04-25T21:46:02.190 に答える
21
private Boolean isOn = false;
public void doSomeStuffAndToggleTheThing(){
   synchronized(isOn){

これはひどい考えです。公開さisOnれているものと同じオブジェクトを参照します。Boolean.FALSE正しく記述されていない他のコードもこのオブジェクトをロックすることを決定した場合、2つの完全に無関係なトランザクションが互いに待機する必要があります。

ロックは、オブジェクトインスタンスを参照する変数ではなく、オブジェクトインスタンスに対して実行されます。

ここに画像の説明を入力してください

于 2012-04-25T21:50:47.100 に答える
1

あなたの問題は、ブール値の同期よりも同期自体にあると思います。各スレッドが道路であり、ステートメント(車)が次々に進むと想像してください。ある時点で交差点が存在する可能性があります。セマフォがないと衝突が発生する可能性があります。Java言語には、これを説明するための組み込みの方法があります。どのオブジェクトも交差点になる可能性があるため、どのオブジェクトにも、セマフォとして機能するモニターが関連付けられています。コードで同期を使用する場合、セマフォを作成しているため、すべての道路(スレッド)に同じセマフォを使用する必要があります。したがって、ブール値が2つしかないため、この問題は実際にはブール値に固有ではありません。この問題は、インスタンス変数で同期し、同じ変数を別のオブジェクトにポイントするたびに発生します。したがって、コードはブール値では間違っていますが、整数でも同様に危険です。

于 2012-04-25T22:28:59.983 に答える
1

すべてのラッパークラスは不変です。それらを同期させてはならない主な理由の1つ。

2つのスレッドがラッパークラスオブジェクトで同期し、そのうちの1つがその値を変更するかのように、新しい/変更されたオブジェクトで同期され、両方のスレッドが2つの異なるオブジェクトで同期されます。したがって、同期の目的全体が失われます。

于 2019-05-29T18:12:54.660 に答える
-3

編集:グレイの答えは正しいです。

私が付け加えたいのは、あなたの建築家は正しいのですBooleanが、不変の観点からすれば、なぜそれを同期させるのですか?ただし、マルチスレッドは複雑で、シナリオに基づいています。

于 2012-04-26T02:47:04.983 に答える