0

これについてウェブを検索してきましたが、これに近い記事を見つけることができず、非常に驚​​いています. 私がまだ見つけていないどこかに知恵が隠されているのかもしれません。

さまざまなタイプの 10 個のメンバーを持つクラスがあり (簡単にするために、int と String が混在しているとします)、それぞれに独自のアクセサー メソッドがあるとします。ここで、このクラスをスレッドセーフにしたいと考えています。ただし、これらのデータ メンバーの一部は、必ずしも相互にやり取りするとは限りません。たとえば、Person以下のクラスにはagenameおよびその他のプロパティがあります。

public class Person {
    private volatile int age;
    private String name;
    private volatile long blabla;
    // ... and so on

    public synchronized int getAge() {
        return age;
    }

    public synchronized void setAge(int age) {
        this.age = age;
    }

    // .. and so on for each data member
}

1 つのスレッドは読み取り/書き込みageのみが必要で、他のスレッドは変更のみが必要な場合がありnameます。明らかに、synchronizedすべてのアクセサー メソッドに追加することは、オブジェクトのインスタンス全体をロックするため、悪い考えです。呼び出しているスレッドは、呼び出してgetAge()いる別のスレッドを待つ必要がありますが、とは 2 つの別個のフィールドです。getName()agename

したがって、明らかな解決策の 1 つは、フィールドごとにロックを作成する (またはvolatileプリミティブ型に追加する) ことです。ただし、これはやり過ぎのようです。10 個のデータ メンバーがある場合、10 個のロックも必要ですか? 過度のロックなしでこれを達成する別の方法があるかどうか疑問に思っています。

4

2 に答える 2

4

プリミティブ型の同期について懸念がある場合、これは AtomicInteger などの優れた使用例です。これらは非常に高速で、スレッド セーフを保証します。詳細については:

http://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html

于 2012-10-02T23:16:13.360 に答える
4

まず、プリミティブ (または のような不変オブジェクト) について話している場合、String必要なのは各フィールドをマークすることだけですvolatile。フィールド値を取得して設定するだけであれば、ロックは必要ありません。

ただし、get/set メソッドが複数の操作を行い、synchronizedブロックが必要な場合、synchronizedフィールドごとにブロックを設定するのは時期尚早の最適化のように思えます。synchronizedあなたのような小さなオブジェクトのメソッドPersonは、これを達成するための完全に適切な方法だと思います. 本当の理由 (つまり、プロファイラーの出力) がない限り、これ以上複雑にするつもりはありません。 確かに、フィールドごとのロックは、ほぼすべての状況でやり過ぎです。

メソッドに時間がかかる場合は、違いがあります。次に、オブジェクト全体をロックして他のアクセサーをブロックしたくないでしょう。次に、複数のロックを用意するのに適した時期です (それぞれ個別の計算用)。しかし、オブジェクトが本当に get/set を保護しようとしているだけなら、synchronizedメソッドは問題ありません。

他のいくつかのコメント:

  • フィールドだけで済む場合は、ブロックvolatileは必要ありません。synchronized
  • メソッドがある場合はsynchronized、フィールドを作成する必要はありませんvolatile
  • フィールドが書き込まれていないかのnameようにマークする必要がある場合。final
于 2012-10-02T23:14:02.047 に答える