web-dev-qa-db-ja.com

同期を使用できる場合はなぜ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;
}
293
adhg

synchronized構文とは異なり、A ReentrantLock非構造化です。つまり、次のようなブロック構造を使用する必要はありません。メソッド間でロックをかけたり、ロックをかけたりすることもできます。例:

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を使用した場合に単に「パフォーマンスが向上する」と仮定するのではなく、同期が不適切であると判明するまで同期を使用して開発することをお勧めします。覚えておいて、これらは上級ユーザーのための高度なツールです。 (そして本当に上級のユーザは、単純なツールが不十分であると確信するまで彼らが見つけることができる最も単純なツールを好む傾向があります。)いつものように、最初にそれをし、次に速くする必要があるかどうかを心配します。

435
oldrinb

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

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

ReentrantReadWriteLockが特別な理由は、プロデューサーとコンシューマーのスレッド同期に関する情報を参照することです。

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

ちなみに、synchronized(this)を使用し、一般にパブリッククラスインスタンスを使用してロックすると、コードが潜在的なデッドロックにさらされるため、他の誰かが故意にオブジェクトに対してロックしようとするため、問題が発生する可能性がありますプログラム。

13
Mike Dinescu

ReentrantLock に関するOracleのドキュメントページから。

暗黙のモニターロックと同じ基本的な振る舞いとセマンティクスを持つ、再入可能な相互排他ロック。

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

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

ReentrantLockの主な機能はこちら の記事

  1. 断続的にロックする機能。
  2. ロックを待っている間にタイムアウトする機能。
  3. 公平なロックを作成するための力。
  4. ロックを待っているスレッドのリストを取得するためのAPI。
  5. ブロックせずにロックを試すための柔軟性。

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

Benjamenによる、この 記事ReentrantLocksの使い方を調べてみてください。

7
Ravindra babu

公平性ポリシーまたはタイムアウトを指定して再入可能ロックを使用すると、スレッドの枯渇を回避できます。スレッド公平性ポリシーを適用できます。それはあなたのリソースに到達するために永遠に待っているスレッドを避けるのを助けるでしょう。

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

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

また、ブロックをエスケープすることができなければ、Synchronizeは無期限にブロックすることができます。再入ロックではタイムアウトを設定できます。

0
j2emanue

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

private static ReentrantLock lock = new ReentrantLock();

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

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

0
RonTLV

覚えておくべきことの一つは、次のとおりです。

'ReentrantLock'という名前は、他のロック機構について誤ったメッセージであり、それらが再入可能ではないことを示します。 これは正しくありません。 'synchronized'を介して取得されたロックも、Javaには再入可能です。

主な違いは、 'synchronized'は組み込みロック(すべてのオブジェクトが持っているもの)を使用するのに対し、Lock APIは使用しないことです。

0
Summer

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

リエントラントロックは非常に柔軟で、スレッドが長時間待機していて現在実行中のスレッドが完了した後であるという公平性ポリシーを持ちます。これにより、待機中のスレッドが共有リソースにアクセスする機会が得られるため、システムのスループットが低下し、時間がかかります。

0
Sumit Kapoor