19
// Not really how java.util.concurrent.Semaphore is implemented
@ThreadSafe
public class SemaphoreOnLock {
    private final Lock lock = new ReentrantLock();
    // CONDITION PREDICATE: permitsAvailable (permits > 0)
    private final Condition permitsAvailable = lock.newCondition();
    @GuardedBy("lock") private int permits;

    SemaphoreOnLock(int initialPermits) {
        lock.lock();
        try {
            permits = initialPermits;
        } finally {
            lock.unlock();
        }
    }

/* other code omitted.... */

Java Concurrency in Practiceリスト 14.12 Lock を使用して実装されたセマフォのカウントから抽出された上記のサンプルについて質問があります。

なぜコンストラクターでロックを取得する必要があるのか​​ 疑問に思っています(lock.lock()が呼び出されるように)。私の知る限り、コンストラクターはアトミックです(エスケープされた参照を除く)。他のスレッドは参照を取得できないため、半分構築されたオブジェクトは他のスレッドから見えません。したがって、コンストラクターの synchronized 修飾子は必要ありません。さらに、オブジェクトが安全に公開されている限り、メモリの可視性についても心配する必要はありません。

では、なぜコンストラクター内で ReentrantLock オブジェクトを取得する必要があるのでしょうか。

4

3 に答える 3

15

半構築オブジェクトは他のスレッドから見えない

それは本当ではない。非 final/volatileフィールドがある場合、オブジェクトは構築時に他のスレッドに表示されます。したがって、他のスレッドは、現在のスレッドと一致しないpermitsieのデフォルト値を参照する可能性があります。0

Java メモリ モデルは、不変オブジェクト (final フィールドのみを持つオブジェクト)の初期化の安全性を特別に保証します。別のスレッドから見えるオブジェクト参照は、必ずしもそのオブジェクトの状態が消費スレッドから見えるという意味ではありません -JCP $3.5.2

Java Concurrency in Practice のリスト 3.15 から:

コンストラクターで設定されたフィールド値は、それらのフィールドに書き込まれる最初の値であり、したがって古い値と見なされる「古い」値はないように見えるかもしれませんが、オブジェクトコンストラクターは、サブクラス コンストラクターが実行される前に、最初にすべてのフィールドにデフォルト値を書き込みます。 . したがって、フィールドのデフォルト値が古い値として表示される可能性があります。

于 2012-05-10T06:33:22.527 に答える
0

正直なところ、メモリフェンスが導入されていることを除けば、ここでのロックの有効な使用法はわかりません。intとにかく、割り当ては32/64ビットでアトミックです。

于 2012-05-10T08:35:03.890 に答える
0

(私自身の貧弱な頭のためにそれを明確にするだけです-他の答えは正しいです)。

この架空SemaphoreOnLockのクラスのインスタンスは、共有されることを意図しています。そのため、スレッドT1はインスタンスを完全に構築し、それをスレッドが参照できる場所に置きT2、フィールドの読み取りが必要なメソッドを呼び出しpermitsます。permitsフィールドについて注意すべきいくつかの重要事項:

  1. 最初のケースでは、デフォルト値の0
  2. 0次に、スレッドによって値が割り当てられます (デフォルトの 以外の場合もあります)。T1
  3. そうではありませんvolatile
  4. そうではありませんfinal(これにより、「ワンショット揮発性」のようなものになります)

したがって、最後T2に書き込まれた値を読み取りたい場合はT1、同期する必要があります。他のすべての場合と同様に、コンストラクターでこれを行う必要があります。(アトミックな割り当てであるかどうかは、この可視性の問題には影響しません)。構築されたものを単一のスレッドに限定する戦略はSemaphoreOnLock、私たちにとってはうまく@Threadsafeいきませ

この例が示しているのは、非静的、非最終、非揮発性フィールドをデフォルト値以外の値に設定する場合、「スレッドセーフであること」がオブジェクトの構築にも適用されることです。

もちろん、@NotThreadsafeクラスがあるときは、これについて考える義務さえありません。呼び出し元が us を構築し、2 つのスレッド間で us を共有することを決定した場合、呼び出し元は適切な同期を手配する必要があります。そのシナリオでは、可視性の問題を心配することなく、コンストラクターで好きなことを行うことができます-それは他の誰かの問題です.

于 2012-12-09T13:56:15.130 に答える