ホームページ  >  記事  >  Java  >  JavaマルチスレッドでLockを使用する方法

JavaマルチスレッドでLockを使用する方法

PHPz
PHPz転載
2023-05-12 14:46:061523ブラウズ

Jdk1.5 以降では、java.util.concurrent.locks パッケージの下に、スレッド同期を実装する一連のインターフェイスとクラスがあります。スレッド同期に関して言えば、誰もが synchronized キーワードを思い浮かべるかもしれません

これは Java の組み込みキーワードであり、スレッドの同期を処理するために使用されます。ただし、このキーワードには多くの欠陥があり、あまり便利ではなく直感的に使用できないため、Lock が表示されます。以下、

説明ロックを比較します。

通常、synchronized キーワードを使用すると、次の問題が発生します。

(1) 制御不能、ロックを自由にロックしたり解放したりできない。

(2) 効率は比較的低いです。たとえば、現在 2 つのファイルを同時に読み取っています。読み込みと読み込みは相互に影響しません。ただし、読み込みオブジェクトに synchronized を使用して同期を実現すると、 ,

したがって、1 つのスレッドが入力される限り、他のスレッドは待機する必要があります。

(3) スレッドがロックを取得したかどうかを知る方法はありません。

Lock は上記の同期の問題をうまく解決できます。jdk1.5 以降では、読み取り/書き込みロックなどのさまざまなロックも提供されていますが、synchronized

## を使用する際に注意すべき点が 1 つあります。 ※重要な場合は手動でロックを解除する必要はありませんが、Lockを使用する場合は手動でロックを解除する必要があります。ロックロックについて学びましょう。

Lock は上位層のインターフェイスであり、そのプロトタイプは次のとおりで、合計 6 つのメソッドが提供されています。前に述べたように、 Lock を使用するには手動でロックを解放する必要がありますが、プログラム内で例外がスローされた場合、ロックは解放できず、デッドロックが発生する可能性があります。次のような固定形式:

public interface Lock {
  // 用来获取锁,如果锁已经被其他线程获取,则一直等待,直到获取到锁
   void lock();
  // 该方法获取锁时,可以响应中断,比如现在有两个线程,一个已经获取到了锁,另一个线程调用这个方法正在等待锁,但是此刻又不想让这个线程一直在这死等,可以通过
    调用线程的Thread.interrupted()方法,来中断线程的等待过程
  void lockInterruptibly() throws InterruptedException;
  // tryLock方法会返回bool值,该方法会尝试着获取锁,如果获取到锁,就返回true,如果没有获取到锁,就返回false,但是该方法会立刻返回,而不会一直等待
   boolean tryLock();
  // 这个方法和上面的tryLock差不多是一样的,只是会尝试指定的时间,如果在指定的时间内拿到了锁,则会返回true,如果在指定的时间内没有拿到锁,则会返回false
   boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  // 释放锁
   void unlock();
  // 实现线程通信,相当于wait和notify,后面会单独讲解
   Condition newCondition();
}

簡単な例を見てみましょう。コードは次のとおりです:

Lock l = ...;
      l.lock();
      try {
        // access the resource protected by this lock
      } finally {// 必须使用try,最后在finally里面释放锁
        l.unlock();
      }

Lock と synchronized の比較

1 、関数

lock と synchronized はどちらも、スレッド セーフティの問題を解決するために Java で使用されるツールです。

2. Source

sychronized は Java のキーワードです。

lock は JUC パッケージで提供されるインターフェイスです。このインターフェイスには、最も一般的に使用される ReentrantLock (リエントラント ロック) を含む多くの実装クラスがあります。

3. ロックの強度

sychronized は 2 つの方法でロックの強度を制御できます:

メソッド レベルで synchronized キーワードを変更します。 はコードブロック上に装飾されています。

ロック オブジェクトの違い:

ロック オブジェクトが静的オブジェクトまたはクラス オブジェクトの場合、このロックはグローバル ロックです。

ロック オブジェクトは通常のインスタンス オブジェクトであり、このロックの範囲はこのインスタンスのライフ サイクルによって異なります。
ロックの強度は、lock() と lock() の 2 つのメソッドによって決まります。 2 つのメソッド間のコードはスレッドセーフであることが保証されています。ロックの範囲は、ロック インスタンスのライフサイクルによって異なります。

4. 柔軟性

ロックは同期よりも柔軟性があります。

lock は、いつロックするか、ロックを解放するかを個別に決定できます。 lock の lock() メソッドと lock() メソッドを呼び出すだけです。

sychronized これはキーワードであるため、ノンブロッキング競合ロック メソッドを実装できません。1 つのスレッドがロックを取得した後、他のロックはロックを取得する前にそのスレッドが解放されるのを待つことしかできません。 。

5. 公平なロックと不公平なロック

公平なロック: 複数のスレッドは、ロックを適用した順序でロックを取得します。スレッドはキューに直接入り、キューに入れて、永遠に。キューの最初の人だけがロックを取得できます。

利点: すべてのスレッドがリソースを取得でき、餓死することはありません。 欠点: スループットが低く、キュー内の最初のスレッドを除いて他のスレッドがブロックされ、ブロックされたスレッドをウェイクアップする際の CPU オーバーヘッドが高くなります。

不公平なロック: 複数のスレッドがロックを取得すると、直接ロックを取得しようとします。取得できない場合は待機キューに入ります。取得できる場合は、直接ロックを取得します。

利点: CPU がスレッドをウェイクアップするオーバーヘッドを削減でき、全体的なスループット効率が向上し、CPU がすべてのスレッドをウェイクアップする必要がないため、ウェイクアップするスレッドの数が減少します。

欠点: キューの途中にあるスレッドはロックを取得できないか、長時間ロックを取得できない可能性があり、最終的には餓死する可能性があります。
lock は、公平なロックと不公平なロック (デフォルトの不公平なロック) の 2 つのメカニズムを提供します。

synchronized は不当なロックです。



6. 例外によりロックを解放するかどうか

同期ロックの解放は受動的であり、同期コード ブロックの実行時にのみ解放されます。終了するか、例外が発生します。

ロック ロックで例外が発生した場合、占有されていたロックは積極的に解放されません。ロックは、unlock() を使用して手動で解放する必要があるため、通常は同期コード ブロックを try-catch に入れて、unlock を書き込みます() デッドロックを回避するメソッド。

7. ロックを取得できるかどうかを確認します。

同期はできません。

lock は、ノンブロッキング競合ロック メソッド trylock() を提供し、戻り値はブール型です。これは、ロックの取得を試行するために使用されることを示します。取得が成功した場合は true を返し、取得が失敗した場合は false を返します。このメソッドは、何があってもすぐに戻ります。

8. スケジュールメソッド

synchronized はオブジェクト自体の wait、notify、notifyAll メソッドを使用し、lock はスレッド間のスケジュールに Condition を使用します。

9. 中断できるかどうか

synchronized はロックが解放されるのを待つことしかできず、割り込みには応答できません。

interrupt() を使用すると、ロックの待機中に割り込むことができます。

10. パフォーマンス

競争が激しくない場合、パフォーマンスはほぼ同じですが、競争が激しい場合、ロックのパフォーマンスが向上します。

Lock ロックでは、readwritelock を使用して読み取りと書き込みを分離することもできるため、マルチスレッド読み取り操作の効率が向上します。

11. 同期ロックのアップグレード

同期コード ブロックは、monitorenter/monitorexit 命令のペアによって実装されます。 Monitor の実装はオペレーティング システム内のミューテックス ロックに完全に依存しており、ユーザー モードからカーネル モードへの切り替えが必要なため、同期操作は未分化の重量級操作となります。

したがって、JVM は 3 つの異なるロック (バイアス ロック、軽量ロック、重量ロック) を提供するようになりました。

バイアス ロック:
競合が発生しない場合、デフォルトでバイアス ロックが使用されます。スレッドは、CAS 操作を使用してオブジェクト ヘッダーにスレッド ID を設定し、オブジェクトが現在のスレッドに偏っていることを示します。

目的: 多くのアプリケーション シナリオでは、ほとんどのオブジェクトのライフ サイクルは最大 1 つのスレッドによってロックされます。バイアスされたロックを使用すると、競合がない場合にオーバーヘッドを削減できます。

軽量ロック:
JVM は、現在のスレッドのスレッド ID と Java オブジェクト ヘッダーのスレッド ID を比較して、一貫性があるかどうかを確認します。一貫性がない場合 (たとえば、スレッド 2 がロック オブジェクト) の場合は、Java オブジェクト ヘッダーのレコードをチェックする必要があります。スレッド 1 が生きているかどうか (バイアスされたロックは積極的に解放されないため、スレッド 1 の格納されたスレッド ID のままです)。生きていない場合は、スレッド 1 が生きているかどうかを確認します。の場合、ロック オブジェクトはまだバイアスされたロックです (オブジェクト ヘッダーの threadID はスレッド 2 です)。存続する場合は、バイアス ロックを取り消し、軽量ロックにアップグレードします。

他のスレッドが軽量ロックを使用してリソースにアクセスする必要がある場合、スピン ロックの最適化を使用してリソースにアクセスします。

目的: ロック オブジェクトをめぐって競合するスレッドは多くなく、スレッドは長時間ロックを保持しません。スレッドをブロックすると、CPU がユーザー モードからカーネル モードに移行する必要があり、コストがかかります。ブロックした直後にロックが解放された場合、利益は損失に見合っていません。したがって、現時点ではスレッドをブロックしない方がよいでしょう。回転させてロックが解除されるのを待ちます。

ヘビーウェイト ロック:
スピンが失敗すると、自己選択が再度失敗する可能性が高いため、スレッドをブロックして CPU 消費量を削減するために、ヘビーウェイト ロックに直接アップグレードされます。

ロックがヘビーウェイト ロックにアップグレードされると、ロックを取得していないスレッドはブロックされ、ブロッキング キューに入ります。

以上がJavaマルチスレッドでLockを使用する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。