synchronized(this) が使えるのに、なぜ ReentrantLock を使うのでしょうか? 質問する

synchronized(this) が使えるのに、なぜ ReentrantLock を使うのでしょうか? 質問する

を使用できる場合、同時実行のロックがなぜそれほど重要なのかを理解しようとしていますsynchronized (this)。以下のダミー コードでは、次のいずれかを実行できます。

  1. メソッド全体を同期するか、脆弱な領域を同期するか(synchronized(this){...}
  2. または、脆弱なコード領域を ReentrantLock でロックします。

コード:

    private final ReentrantLock lock = new ReentrantLock(); 
    private static List<Integer> ints;

    public Integer getResult(String name) { 
        .
        .
        .
        lock.lock();
        try {
            if (ints.size()==3) {
                ints=null;
                return -9;
            }   

            for (int x=0; x<ints.size(); x++) {
                System.out.println("["+name+"] "+x+"/"+ints.size()+". values >>>>"+ints.get(x));
            }

        } finally {
            lock.unlock();
        } 
        return random;
}

ベストアンサー1

再入ロック構造化されておらず、synchronizedコンストラクトとは異なります。つまり、ロックにブロック構造を使用する必要がなく、メソッド間でロックを保持することもできます。例:

private ReentrantLock lock;

public void foo() {
  ...
  lock.lock();
  ...
}

public void bar() {
  ...
  lock.unlock();
  ...
}

このようなフローは、コンストラクト内の単一のモニターで表現することは不可能ですsynchronized


それ以外にも、ReentrantLockサポートロックポーリングそしてタイムアウトをサポートする割り込み可能なロック待機.ReentrantLockもサポートしています設定可能な公平性ポリシーより柔軟なスレッド スケジューリングが可能になります。

このクラスのコンストラクタは、オプションのfairnessパラメータを受け入れます。 が設定されている場合true、競合時に、ロックは最も長く待機しているスレッドへのアクセスを許可します。それ以外の場合、このロックは特定のアクセス順序を保証しません。多くのスレッドによってアクセスされる fair lock を使用するプログラムは、デフォルト設定を使用するプログラムよりも全体的なスループットが低くなる可能性があります (つまり、遅くなります。多くの場合、非常に遅くなります) が、ロックを取得する時間の変動は小さくなり、スタベーションがなくなることが保証されます。ただし、ロックの公平性は、スレッド スケジューリングの公平性を保証するものではありません。したがって、フェア ロックを使用する多くのスレッドの 1 つが、他のアクティブなスレッドが進行しておらず、現在ロックを保持していない間に、ロックを連続して複数回取得する場合があります。また、untimedtryLockメソッドは公平性設定を尊重しないことにも注意してください。他のスレッドが待機している場合でも、ロックが使用可能であれば成功します。


ReentrantLock かもしれないよりスケーラブル競合率が高い場合のパフォーマンスが大幅に向上します。詳細については、ここ

しかし、この主張には異論があります。次のコメントを参照してください。

再入可能ロック テストでは、毎回新しいロックが作成されるため、排他ロックは行われず、結果のデータは無効になります。また、IBM リンクでは基礎となるベンチマークのソース コードが提供されていないため、テストが正しく実行されたかどうかさえ判断できません。


sはいつ使用すべきでしょうかReentrantLock? developerWorks の記事によると...

答えは非常に簡単です。synchronized時間指定のロック待機、割り込み可能なロック待機、非ブロック構造のロック、複数の条件変数、ロック ポーリングなど、 では提供されているが では提供されていない機能が実際に必要なときに使用してください。 にはReentrantLockスケーラビリティの利点もあるため、実際に競合が頻繁に発生する状況の場合は を使用する必要がありますが、大多数のsynchronizedブロックでは競合はほとんど発生せず、競合が頻繁に発生することはまずないことに注意してください。 を使用すれば「パフォーマンスが向上する」と単純に想定するのではなく、同期が不十分であることが証明されるまで、同期を使用して開発することをお勧めしますReentrantLock。 これらは上級ユーザー向けの高度なツールであることを忘れないでください。(そして、本当に上級のユーザーは、単純なツールが不十分であると確信するまで、見つけられる最も単純なツールを好む傾向があります。) いつものように、最初に正しく作成してから、高速化する必要があるかどうかを心配してください。


近い将来、より重要になるであろう最後の側面は、Java 15 と Project LoomReentrantLock仮想スレッドの(新しい)世界では、基礎となるスケジューラはよりも の方がはるかにうまく機能することができますsynchronized。これは少なくとも Java 15 の初期リリースでは当てはまりますが、後で最適化される可能性があります。

現在の Loom 実装では、仮想スレッドは 2 つの状況で固定できます。スタック上にネイティブ フレームがある場合 (Java コードがネイティブ コード (JNI) を呼び出し、ネイティブ コードが Java をコールバックする場合)、およびsynchronizedブロックまたはメソッド内にある場合です。これらの場合、仮想スレッドをブロックすると、それを運ぶ物理スレッドもブロックされます。ネイティブ呼び出しが完了するか、モニターが解放されると (ブロックsynchronized/メソッドが終了すると)、スレッドの固定は解除されます。

によって保護された一般的な I/O 操作がある場合はsynchronized、モニターによるピン留めを修正する前であっても、アプリケーションが Loom のスケーラビリティ向上のメリットを十分に享受できるように、モニターを に置き換えます (または、可能であればReentrantLock、より高性能な を使用します)。StampedLock

おすすめ記事