11

私が書いたいくつかの重要なクラスで、スレッドセーフについて心配する必要があるかどうかを判断しようとしています。私はいくつかの記事/既存の SO の質問を読みましたが、スレッド セーフの同じ繰り返し定義を見続けています。

スレッド セーフとは、オブジェクトまたはクラスのフィールドが、複数のスレッドで同時に使用された場合でも、他のオブジェクトやクラスから観察されるように、常に有効な状態を維持することを意味します。

わかった。なんとなくわかります。しかし、私がここで見逃しているパズルの非常に大きなピースがあります。

スレッドセーフは、クラスの同じインスタンスが複数のスレッドで使用されている場合、または2つ以上のインスタンスが使用されている場合にのみ機能しますか?


例 #1:

Dog静的メソッドまたはフィールドを含まないクラスがあり、5 つの異なるスレッド内から操作されている a の 5 つの異なるインスタンスがあるとDogします。スレッドセーフについて心配する必要はありますか? Dog静的フィールド/メソッドがなく、各スレッドには、他の 4 つのインスタンスとは無関係に状態が存在する独自のインスタンスがあるため、「いいえ」と言うでしょう。(1) これは正しいですか。そうでない場合、なぜですか?


例 #2:

に静的メソッドを追加するとしますDog

public void woof() {
    this.isWoofing = true;
}

public static void makeWoof(Dog dog) {
    dog.woof();
}

今、スレッドセーフについて心配する必要がありますか? 各スレッドには の独自のインスタンスがありますがDog、現在は同じ静的makeWoof()メソッドを共有しており、Dogそれが動作している の状態を変更します。私はまだ「いいえ」と言います。(2) これは正しいですか。そうでない場合、なぜですか?

これら 2 つの例を考えると、複数のスレッドがクラスの同じインスタンスで動作している場合にのみ、スレッド セーフが問題になるように思えます。しかし、各スレッドに独自のインスタンスを与えるとすぐに、そのインスタンスに対してやりたいことが何でもできるようになり、他のスレッド内で何が起こっているかを気にする必要がなくなります。(3) これは正しいですか。そうでない場合、なぜですか?これに対する例外はありますか?前もって感謝します!

4

5 に答える 5

5

スレッドセーフに関するあなたの仮定は正しいです。instance複数のスレッドによって変更されている同じフィールド/状態に要約されます。

最初の質問では、スレッドはすべて独自のインスタンスを操作するDogため、アプローチはスレッドセーフです。

2 番目の質問では、インスタンスが静的メソッドに渡されるため、もう一度クラスのさまざまなインスタンスを操作します。クラスに静的フィールドが含まれていて、静的メソッドがそれらのフィールドを操作した場合、Dogそれらのフィールドはスレッド セーフではありませんが、非 final 静的フィールドは実際にはスレッド セーフではありません。

于 2013-08-30T15:56:02.463 に答える
5

それは実際には共有インスタンスに関するものではありません。共有状態についてです

それを念頭に置いて:

1: 正しい - スレッドが共有状態で動作しない場合、それらは本質的にスレッド セーフです。

2: 不正解 (一部): - この特定の静的メソッドの特定の例では、共有状態は変更されませんが、静的メソッドは共有状態を作成/操作できます。

3: 1 と 2 を参照

Dog例として、OP のクラスに共有状態を少し導入してみましょう。

    static Integer numberOfBarksToday=0; 

静的であるため、共有されます。したがって、静的メソッド(OPのmakeWoofメソッドの修正版)はそれを操作できます:

    public static void makeWoof(Dog dog) {
        dog.woof();
        synchronized(Dog.numberOfBarksToday) {
            Dog.numberOfBarksToday++;
        }
    }       

上記の例を作成したときに、習慣からアクセスを同期したことに気付きました。その同期により、この特定のアクセスはスレッドセーフです (もちろん、numberOfBarksToday への他のすべてのアクセスも同様に同期する必要があります)。

複数のスレッドがこのメソッドを呼び出している状態で同期を行わないと、今日の鳴き声の数を過小評価する傾向があります: T0) numberOfBarksToday=0; T1) スレッド A はバーク数 ( の最初の部分++) をチェックし、0 を取得します。 T2) スレッド B はバーク数をチェックし、0 を取得します。 T3) スレッド A はバーク数を 1 に設定します T4) スレッド B はバーク数を 1 に設定します

これは、共有オブジェクトで割り当て方法がアトミックかどうかを考慮していません。

同期は上記のすべてを防止し、さらにメモリ バリアを導入して、すべてのスレッドが numberOfBarksToday に対して同じ値を参照できるようにします。

于 2013-08-30T15:57:21.213 に答える
1

例 #1 - あなたは正しいです。

あるオブジェクトがすべて 1 つのスレッドにしか見えないことを示すことができれば、それはスレッドに限定されていると言われます。スレッドに制限されたオブジェクトにはスレッドセーフの問題はありません...そしてDogインスタンスはスレッドに制限されています。

例 #2 - あなたは正しいです。

メソッドを使用しているという事実は、staticここでは何も変更しません。あなたが説明した例を考えると、Dogインスタンスはまだスレッドに制限されています。

例 #3 - 検討中のすべてのオブジェクトがスレッドに限定されていると仮定すると、例外はありません。


woof例 2 のメソッドを共有状態 (static変数など)を使用するように変更した場合、スレッドセーフが問題になる可能性があることに注意してください。

もう 1 つの注意点は、インスタンスがスレッドに制限されるかどうか、またいつになるかを知るのが難しい場合があることです。これに対処するための 2 つの戦略があります。

  • Dog関連するメソッドをスレッドセーフにすることで、クラスをスレッドセーフにすることができます。つまり、使用パターンを分析する必要はありませんが、アプリケーションで不要な同期が行われる可能性があります。

  • 必要に応じて、クラスを使用Dogして外部同期するクラスの手に任せることができます。

于 2013-08-30T16:10:18.007 に答える
0

(1) はい

(2) はい、メソッドが非 final 静的フィールドで動作しない場合。いいえ、そうでなければ

(3) はい、例外については (2) を参照してください。

于 2013-08-30T15:59:30.087 に答える