352

を使用できる場合、並行性のロックが非常に重要になる理由を理解しようとしています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;
}
4

8 に答える 8

522

ReentrantLockは、構造とは異なり、構造化されsynchronizedていません。つまり、ロックにブロック構造を使用する必要はなく、メソッド間でロックを保持することもできます。例:

private ReentrantLock lock;

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

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

このようなフローは、構成内の単一のモニターを介して表すことは不可能synchronizedです。


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

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


ReentrantLock また、よりスケーラブルであり、より高い競合の下ではるかに優れたパフォーマンスを発揮する可能性があります。これについて詳しくは、こちらをご覧ください。

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

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


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

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


近い将来、より関連性が高くなる最後の側面の1つは、Java15とProjectLoomに関係していますReentrantLock仮想スレッドの(新しい)世界では、基盤となるスケジューラーは、それよりもはるかにうまく機能することができsynchronizedます。これは、少なくとも最初のJava 15リリースでは当てはまりますが、後で最適化される可能性があります。

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

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

于 2012-08-06T02:29:48.763 に答える
14

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

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

何が特別なのかをよりよく理解したい場合はReentrantReadWriteLock、生産者/消費者スレッドの同期に関する情報を調べてください。

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

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

于 2012-08-06T02:10:55.810 に答える
13

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

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

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

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

この記事によるReentrantLockの主な機能

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

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

さまざまなタイプのReentrantLocksの使用法に関するBenjamenによるこの記事をご覧ください

于 2016-04-24T18:29:07.177 に答える
3

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

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

于 2018-10-06T12:34:41.897 に答える
2

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

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

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

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

于 2016-06-18T18:59:15.150 に答える
1

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

」 という名前は、他のロックメカニズムについて、再入可能ではないという誤ったメッセージを出すことです。本当じゃない。「同期」を介して取得されたロックは、Javaでも再入可能です。

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

于 2019-05-22T05:15:05.560 に答える
0

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

private static ReentrantLock lock = new ReentrantLock();

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

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

于 2018-11-14T07:39:39.207 に答える
0

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

于 2020-04-16T01:02:17.597 に答える