同期(これ)を使用できるのに、なぜReentrantLockを使用するのですか?


317

私が使用できる場合、並行性のロックが非常に重要になる理由を理解しようとしています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
ところで、すべてのJava組み込みロックは本質的に再入可能です。
Aniket Thakur

@pongapundit synchronized(this){synchronized(this){//some code}}はデッドロックを引き起こしません。組み込みロックの場合、リソースのモニターを取得し、それが再度必要な場合、デッドロックなしで取得できます。
Aniket Thakur

回答:


474

A ReentrantLockのは、ある非構造化とは異なり、synchronized構造-すなわちあなたがロックするためのブロック構造を使用する必要はありませんとさえメソッド間でロックを保持することができます。例:

private ReentrantLock lock;

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

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

このようなフローは、synchronized構成内の単一のモニターでは表現できません。


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

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


ReentrantLock また、よりスケーラブルで、より高い競合状況でのパフォーマンスが大幅に向上する可能性があります。詳細については、こちらをご覧ください

ただし、この主張には異議が唱えられています。次のコメントを参照してください。

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


いつReentrantLocks を使うべきですか?そのdeveloperWorks記事によると...

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


26
lycog.comへの「よりスケーラブルであることがわかっている」リンクは削除する必要があります。再入可能ロックテストでは、毎回新しいロックが作成されるため、排他ロックは行われず、結果のデータは無効になります。また、IBMリンクは基礎となるベンチマークのソースコードを提供していないため、テストが正しく実施されたかどうかを特徴付けることは不可能です。個人的には、主張全体が本質的にサポートされていないため、スケーラビリティに関する行全体を削除します。
Dev

2
私はあなたの反応を考慮して投稿を修正しました。
oldrinb 2013

6
パフォーマンスに大きな懸念がある場合は、同期をまったく必要としない方法を探すことを忘れないでください。
mcoolive 2014年

2
パフォーマンスは私にはまったく意味がありません。再入可能ロックのパフォーマンスが向上する場合、なぜ再入可能ロックが同じ方法で実装されないだけで同期されないのでしょうか?
tObi 2017年

2
@ user2761895 ReentrantLockPseudoRandomLycogリンクのコードは、ブランドの新しい、競合しないロックにのすべての呼び出しを使用しているsetSeednext
oldrinb

14

ReentrantReadWriteLockは専用ロックsynchronized(this)ですが、汎用ロックです。それらは似ていますが、まったく同じではありません。

synchronized(this)代わりに使用できるという点で正しいですReentrantReadWriteLockが、その逆は常に当てはまるとは限りません。

ReentrantReadWriteLockプロデューサー/コンシューマースレッドの同期に関する情報を特別に検索する要素について理解を深めたい場合。

一般に、メソッド全体の同期と汎用の同期(synchronizedキーワードを使用)は、同期のセマンティクスをあまり意識せずにほとんどのアプリケーションで使用できますが、コードからパフォーマンスを絞り出す必要がある場合は、他のよりきめの細かい、または特別な目的の同期メカニズムを探索します。

ちなみに、使用するsynchronized(this)と、一般的にはパブリッククラスのインスタンスを使用してロックすると、コードが潜在的なデッドロックにさらされるため、問題が発生する可能性があります。


:他の誰かが故意にこのようprogameの使用にどこか他のあなたのオブジェクトに対して同期モニターとしてプライベートオブジェクトのインスタンスをロックしようとする可能性がありませんので、潜在的なデッドロックを防ぐために public class MyLock { private final Object protectedLongLockingMonitor = new Object(); private long protectedLong = 0L; public void incrementProtectedLong() { synchronized(protectedLongLockingMonitor) { protectedLong++; } } }
sushicutta

9

ReentrantLockに関するoracleドキュメントページから:

基本的な動作とセマンティクスが、同期されたメソッドとステートメントを使用してアクセスされる暗黙的なモニターロックと同じですが、拡張機能を備えた、再入可能な相互排他ロック。

  1. A ReentrantLockのは、最後に正常にロックするスレッドが所有しているが、まだそれをアンロックされていません。ロックを呼び出しているスレッドは、ロックが別のスレッドによって所有されていない場合に戻り、ロックを正常に取得します。現在のスレッドがすでにロックを所有している場合、メソッドはすぐに戻ります。

  2. このクラスのコンストラクターは、オプションの公平性パラメーターを受け入れます。trueに設定すると、競合下で、 ロックは最も長く待機しているスレッドへのアクセスの許可を優先します。それ以外の場合、このロックは特定のアクセス順序を保証しません。

この記事にあるReentrantLockの主要機能

  1. 割り込みロック機能。
  2. ロックの待機中にタイムアウトする機能。
  3. 公正なロックを作成する力。
  4. ロックを待機しているスレッドのリストを取得するAPI。
  5. ブロックせずにロックを試す柔軟性。

ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLockを使用して読み取りおよび書き込み操作の詳細なロックの制御をさらに取得できます 。

さまざまなタイプのReentrantLocksの使用について、Benjamenによるこの記事をご覧ください。


2

公平性ポリシーまたはタイムアウトで再入可能なロックを使用して、スレッドの枯渇を回避できます。スレッドの公平性ポリシーを適用できます。これは、スレッドがリソースに到達するために永久に待機することを回避するのに役立ちます。

private final ReentrantLock lock = new ReentrantLock(true);
//the param true turns on the fairness policy. 

「公平性ポリシー」は、実行する次の実行可能なスレッドを選択します。優先度、前回の実行からの経過時間に基づいています。

また、Synchronizeは、ブロックをエスケープできない場合は無期限にブロックできます。Reentrantlockにはタイムアウトを設定できます。


1

同期ロックは、1つのスレッドの実行後に並列に実行されているすべてのスレッドがロックを取得できる待機キューのメカニズムを提供しません。そのため、システムに存在し、より長い時間実行されているスレッドが共有リソースにアクセスする機会を得ることはなく、飢餓につながります。

再入可能ロックは非常に柔軟であり、公平性ポリシーがあり、スレッドがより長い時間待機していて、現在実行中のスレッドの完了後に、より長い待機中のスレッドが共有リソースにアクセスする機会を確実に得ることができます。システムのスループットとより多くの時間を費やしています。


1

このコードがスレッドで実行されていると仮定しましょう:

private static ReentrantLock lock = new ReentrantLock();

void accessResource() {
    lock.lock();
    if( checkSomeCondition() ) {
        accessResource();
    }
    lock.unlock();
}

スレッドはロックを所有しているため、lock()への複数の呼び出しを許可するため、ロックに再び入ります。これは参照カウントで実現できるため、再度ロックを取得する必要はありません。


0

覚えておくべきことの1つは、

ReentrantLock」という名前は、他のロックメカニズムについて、それらが再入可能ではないという誤ったメッセージを表示することです。本当じゃない。'synchronized'を介して取得されたロックもJavaで再入可能です。

主な違いは、「同期」は固有のロック(すべてのオブジェクトが持つロック)を使用するのに対し、ロックAPIは使用しないことです。


0

wait / notify / notifyAllメソッドは、めったに使用されないメソッドですべてのオブジェクトを汚染するため、Objectクラスに属していないと思います。専用のLockクラスではるかに理にかなっています。したがって、この観点からは、おそらく、目前の仕事用に明示的に設計されたツール、つまりReentrantLockを使用する方が良いでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.