9

クラス(String、StringBufferなど)の作成を考えていました。これは、マルチスレッド環境だけでなくシングルスレッド環境でも使用できます。開発者がどのような環境で使用しているのかわかりません。最悪の場合を想定して、同期できます。

ただし、 1. 同期によりパフォーマンスが低下します。2. 同期がないと、スレッドセーフではありません。

だから、私には2つの選択肢があります。

  1. クラスを非同期のままにしますが、このクラスを使用する開発者は、必要に応じてクラスを同期する必要があります。
  2. すべてのメソッドを同期して、パフォーマンスに影響を与えます。

私は、Java の多くの (すべてではないにしても、Vector に対する ArrayList など) クラスが最初のアプローチを取るように進化したことを見てきました。クラスでこれら 2 つのオプションを決定する前に、考慮すべきことは何ですか?

または、別の言い方をすれば、バーがマルチスレッド環境で使用でき、実行すべきではないことが確実にわかっている場合にのみ、「public void bar()」よりも「public synchronized void bar()」を使用する必要があります。同時に?

編集 したがって、明らかに、見出しで「ユーティリティクラス」という言葉を誤って使用しました。指摘してくれてありがとう、Jon Skeet。見出しから世界の「ユーティリティ」を削除しました。

例を挙げると、カウンターのようなクラスについて考えていました。カウンターは一例です。Counter を実装する方法は他にもあります。しかし、この質問は同期に関するものです。Counter オブジェクトは、何かが実行された回数を追跡します。ただし、シングルスレッドまたはマルチスレッド環境で使用できる可能性があります。では、カウンターでの同期の問題をどのように処理すればよいでしょうか。

4

5 に答える 5

11

私がユーティリティ クラス考えているもの(通常、漠然と関連する public static メソッドの寄せ集め) は、ほとんど同期を必要としません。クラスが変更可能な状態を維持しない限り、通常はまったく問題ありません。

もちろん、それ自体がスレッド間で共有され、変更可能な状態を含むパラメーターを取得する場合は、同期が必要になる場合がありますが、通常は呼び出し元が決定する必要があります。

「ユーティリティ クラス」で何か他のことを意味している場合は、その意味を知っておくとよいでしょう。変更可能な状態のないクラス (ただし、構築時に設定される不変の状態の可能性があります) の場合、通常はスレッド間で共有しても問題ありません。

ミュータブルな状態を持っているが、明示的にスレッド化について言及していない場合、私は通常、そのクラス内に同期を配置しませんが、スレッドセーフではないことを文書化します。通常、呼び出し元はとにかく複数のオブジェクトを使用して複数の操作を同期する必要があるため、「一度にメソッド」の同期は通常役に立ちません。

それがすべてスレッドに関するクラスである場合(たとえば、プロデューサー/コンシューマーキューを管理するためのもの)、スレッドセーフにしようとしますが、それが何を意味するかを文書化します。メソッド自体を同期するのではなく、同期にのみ使用されるプライベート final フィールドで同期することをお勧めします。そうすれば、そのオブジェクトで同期できる可能性のある唯一のコードがクラスに含まれ、ロックについての推論が容易になります。

ほとんどの場合、パフォーマンスに基づいてこれらの決定を行うべきではありません。ほとんどの場合、正確さと簡単さはパフォーマンスよりもはるかに重要です。

于 2012-11-01T20:19:19.550 に答える
3

最後の質問について: 複数のスレッドから呼び出すことができる場合、メソッドを同期しません。複数のスレッドからアクセスできる状態を使用する場合、メソッドを同期します。

したがって、bar()メソッドが 1 つのスレッドからのみ呼び出されたとしても、複数のスレッドによって他のメソッドを介して読み取られ、変更されるインスタンス変数にアクセスする場合は、bar()メソッドを同期する必要があります (または少なくとも、アクセスするコードのブロック共有変数)。同期は共有状態に関するものです。

編集:

主な問題について: コレクション フレームワークと同じ戦略を単純に使用できます: Counter をインターフェイスにし、デフォルトの非スレッド セーフな実装を提供します。Countersまた、同期されたカウンター プロキシを返すメソッドを含むユーティリティ クラス ( ) も提供しますCounters.synchronizedCounter(Counter counter);

このようにして、両方の長所を活用できます。この設計の重要な点は、同期されたカウンターがそれ自体で同期されることに注意してください。これにより、カウンターでの 2 つのメソッド呼び出しをアトミックな方法で行う必要がある場合に、呼び出し元が外部同期を追加できるようになります。

Counter synchronizedCounter = Counters.synchronizedCounter(c);
// call a and b atomically:
synchronized (synchronizedCounter) {
    synchronizedCounter.a();
    synchronizedCounter.b();
}
于 2012-11-01T20:26:34.413 に答える
1

同期のパフォーマンスが改善された一方で、VM の他の部分も改善されました。したがって、同期は依然として顕著なオーバーヘッドです。

特に、同期アクションは多くの最適化トリックを防ぎます。VM がエスケープ解析を実行できる場合でも、VM は Java メモリ モデルに準拠するために、並べ替えを差し控え、メモリ バリアを追加する必要があります。

于 2012-11-01T20:39:55.567 に答える
0

最近では、同期による「パフォーマンス ヒット」は非常に小さくなっています。同期がパフォーマンスの問題を引き起こしているという証拠がある場合にのみ、これについて考慮する必要があります。

複数のメソッドによって参照される静的フィールドを介してクラスに状態がある場合、同期が必要になることがあります。この場合、クラスの意図が何であるかを他のプログラマにより明確に伝えるために、インスタンス フィールドを持ち、シングルトン パターンを使用することが望ましいでしょう。

于 2012-11-01T20:21:52.920 に答える
0

同期されたメソッドへのシングル スレッド アクセスのパフォーマンスの低下は、最新の JVM ではほとんど無視できます。

しかし、ベンチマークを作成して自分の目で確かめてみませんか?

于 2012-11-01T20:22:13.420 に答える