Javaのロックは何ですか
<Java Concurrentプログラミング>を読んだ後、この質問に答えることができませんでした。これは、ロックの概念について十分に理解していないことを示しています。だから私は本の内容を再び見て、突然額を開けたように感じました。学ぶための最良の方法は、問題で学び、それらを解決することだと思われます。
Javaには、ロックには2つの主要なタイプがあります。内部ロック同期と表示ロックJava.util.concurrent.locks.lock。しかし、慎重に考えると、要約は正しくないようです。 Javaビルトインロックと同時によって実装される一連のロックである必要があります。
Javaではすべてがオブジェクトであり、Javaには各オブジェクトにロックが組み込まれており、オブジェクトロック/内部ロックと呼ばれることもあります。関連するロック操作は、同期によって完了します。
同期の実装と同時シナリオの複雑さの欠陥のため、誰かが明示的なロックを開発し、これらのロックはjava.util.concurrent.locks.locksから派生しています。もちろん、JDK1.5以降のバージョンに組み込まれています。
同期
まず、より頻繁に使用される同期を見てみましょう。また、私の毎日の仕事でも使用されています。同期は、特定のコードブロックのロックメカニズムを提供するために使用されます。暗黙的にJavaオブジェクトにロックがあります。このロックは、本質的またはモニターロックと呼ばれます。スレッドは、コードが完了した後にロックが自動的にリリースされるまで、同期によって保護されたブロックを入力する前にこのロックを自動的に取得します(または例外としてもロックが自動的にリリースされます。内蔵ロックは相互に排他的です。ロックは同時に1つのスレッドでのみ保持できます。これは複数のスレッドにもつながり、ロックの後ろのスレッドは保持された後にブロックされます。これにより、コードのスレッドの安全性が原子性を確保できるようになります。
再入力します
Javaビルトインロックは相互に排他的であり、後続のスレッドは閉塞を引き起こすため、ロックを取得しようとするときにロックを保持するスレッドが再び入るとどうなりますか?たとえば、次の状況のいずれか:
public class Baseclass {public synchronized void do(){system.out.println( "is base"); }} public class sonclassはbaseclass {public synchronized void do(){system.out.println( "is Son"); super.do(); }} sonclass son = new sonclass(); son.do();この時点で、派生クラスのDOメソッドは最初にロックを1回保持し、次に再度ロックに入り、super.do()を呼び出すときにそれを保持します。ロックが相互に排他的である場合、現時点ではデッドロックする必要があります。
しかし、結果はそうではありません。なぜなら、内部ロックにはリエントラントの特性、つまりロックがリエントラントメカニズム、参照カウント管理を実装するからです。スレッド1がオブジェクトのロックAを保持すると、ロックAへの参照は1を追加することで計算されます。スレッド1が再びロックAを取得すると、スレッド1がロックAを保持する場合、計算は1。
同期のいくつかの機能
コードを変更する方法
変更方法
public class Baseclass {public synchronized void do(){system.out.println( "is base"); }}これは、メソッドを直接ロックすることを意味し、このメソッドブロックを入力するときにロックを取得する必要があります。
コードブロックを変更します
パブリッククラスBaseclass {private static object lock = new object(); public void do(){synchronized(lock){system.out.println( "is base"); }}}ここでは、ロックの範囲がメソッドの一部のコードブロックに縮小され、ロックの柔軟性が向上します。結局のところ、ロックの粒度制御もロックの重要な問題です。
オブジェクトロックのタイプ
一部のコードは特別な用語で同期していることをよく見て、次のコードを見てください。
パブリッククラスBaseclass {private static object lock = new object(); public void do(){synchronized(lock){}} public synchronized void dovoid(){} public synchronized static void dostaticvoid(){} public static void dostaticvoid(){synchronized(baseclass.class){}}}}}}}}ここには、コードブロックの変更、メソッドの変更、静的メソッドの変更、ベースクラスのクラスオブジェクトの変更という4つの状況があります。では、これらの状況の違いは何ですか?
コードブロックを変更します
この場合、コードに同期(ロック)を使用してオブジェクトロックを作成します。これは、オブジェクトの組み込みロックを使用することを意味します。この場合、ロックコントロールはオブジェクトに引き渡されます。もちろん、これを行う別の方法があります:
public void do(){synchronized(this){system.out.println( "is base"); }}これを使用することは、現在のオブジェクトのロックを意味します。内蔵ロックの鍵についてもここに記載されています。このコードを保護するためのロックを提供します。どのスレッドが来ても、同じロックに直面します。
オブジェクトを変更する方法
この直接的な変更の状況はどうですか?実際、これはデフォルトでの現在のオブジェクトのロックであることを除いて、コードブロックの変更に似ています。これにより、コードを書くことは比較的簡単で明確です。前述のように、コードブロックの変更の違いは、主に粒度の制御の違いです。
静的方法を変更します
静的方法について何か違うことはありますか?それは確かに異なっています。この時点で取得されたロックはこれではなく、このオブジェクトが指すクラスはクラスロックです。 Javaのクラス情報はメソッド定数領域にロードされるため、グローバルは一意です。これは実際にグローバルロックを提供します。
変更されたクラスのクラスオブジェクト
この状況は、静的メソッドを変更するときと実際には非常に似ていますが、それでも同じ理由です。この方法では、より柔軟な制御粒度を提供できます。
まとめ
これらの状況の分析と理解を通じて、内蔵ロックの主なコアコンセプトは、相互に排他的に使用できるロックをコードに提供し、スイッチと同様の関数を再生することであることが実際にわかります。
Javaは、内蔵ロックのいくつかの実装も提供します。主な機能は、Javaがすべてオブジェクトであり、各オブジェクトにロックがあるため、状況に応じて使用するロックを選択できることです。
java.util.concurrent.locks.lock
以前に同期したことを見ました。ほとんどの場合、ほぼ十分です。ただし、このシステムは、同時プログラミングでますます複雑になっているため、同期処理がより困難なシナリオは常に多くあります。または、<Java Concurrentプログラミング>に記載されているように、同時のロックは内部ロックを補完し、より高度な機能を提供します。
java.util.concurrent.locks.lockの簡単な分析
このインターフェイスは、ロックの主要な動作を抽象化するため、ロックから派生したロックがこれらの基本的な特性を持つことができます:無条件、サイクリブル、タイミング、割り込み可能。さらに、ロックおよびロック解除操作は明示的に実行されます。これがそのコードです:
パブリックインターフェイスロック{void lock(); boid lockinterrumdibly()throw interurnedexception; boolean tryLock(); Boolean TryLock(長い時間、TimeUnitユニット)が断続的なエクセプトをスローします。 void lock(); condition newcondition();} REENTRANTLOCK
ReentrantLockはReentrant Lockです。名前でさえ非常に明確です。 ReentrantLockは同様のセマンティクスを同期させますが、REENTRANTLOCKは次のような明示的に呼ばなければなりません。
パブリッククラスBaseclass {private lock = new ReentrantLock(); public void do(){lock.lock(); try {// ..}最後に{lock.unlock(); }}}この方法はコードの読み取りでは非常に明確ですが、問題があります。つまり、最終的に試してみるのを忘れたり、lock.unlock()を書き留めたりすると、ロックがリリースされないようになり、デッドロックにつながる可能性があります。同期するリスクはありません。
TryLock
ReentrantLockはロックインターフェイスを実装するため、TryLockなどの機能を自然に備えています。 TryLockは、ロックを取得しようとすることです。ロックが他のスレッドによって占有されている場合、すぐにFALSEを返します。そうでない場合は、占有されて真実を返す必要があります。つまり、ロックが取得されたことを意味します。
別のTryLockメソッドにはパラメーターが含まれています。この方法の機能は、時間を指定することです。つまり、この期間中にロックを取得しようとし続け、時間が得られていない場合はあきらめます。
TryLockは常にブロックしてロックを待つとは限らないため、デッドロックの発生をさらに回避できます。
途切れやすい
ロック途中で、スレッドがロックを取得すると、割り込みに応答します。割り込みが検出された場合、割り込みの例外が上層コードによってスローされます。この場合、丸いロビンロックの出口メカニズムが提供されます。中断可能なロック操作をよりよく理解するために、それを理解するためにデモが書かれました。
パッケージcom.test; import java.util.date; import java.util.concurrent.locks.reentrantlock; public class testLockInterractibly {static reintentrantlock lock = new ReentrantLock(); public static void main(string [] args){thread thread1 = new runnable(){@override public void run(){try {doprint( "swreat 1 get lock。"); doprint( "thread 1 end。");} catch(furtterededexception e){doprint(intertered( ");スレッドスレッド2 = newスレッド(new runnable(){@Override public void run(){try {doprint( "thread 2 get lock。"); do123(); doprint( "thread 2 end。");} catch(interruptedexception e){doprint( "スレッド2は中断されます。 thread1.setname( "thread1"); thread2.setname( "thread2"); thread1.start(); try {thread.sleep(100); //しばらく待って、thread2の前でthread1を実行するようにします} catch(arternedexception e){e.printstacktrace(); } thread2.start(); } private static void do123()throws arturnedexception {lock.lock interrumdibly(); doprint(thread.currentthread()。getname() + "is locked。"); try {doprint(thread.currentthread()。getname() + "dosoming1 ...."); thread.sleep(5000); //スレッドの順序の表示を容易にするために数秒をウェアするドプリント(thread.currentthread()。 doprint(thread.currentThread()。getName() + "IS finent。"); }最後に{lock.unlock(); }} private static void doprint(string text){system.out.println((new date())。tolocalestring() + ":" + text); }}上記のコードには2つのスレッドがあります。 thread1はthread2よりも早く開始します。ロックプロセスを確認するために、ロックコードは5秒間スリープされているため、ロック取得プロセスに入る1番目と2番目のスレッドのプロセスを感じることができます。上記のコードの最終結果は次のとおりです。
2016-9-28 15:12:56:スレッド1ロックを取得します。
2016-9-28 15:12:56:Thread1がロックされています。
2016-9-28 15:12:56:Thread1 dosoming1 ....
2016-9-28 15:12:56:スレッド2ロックを取得します。
2016-9-28 15:13:01:Thread1 Dosoming2 ....
2016-9-28 15:13:01:Thread1が終了しました。
2016-9-28 15:13:01:スレッド1がアンロードされています。
2016-9-28 15:13:01:Thread2がロックされています。
2016-9-28 15:13:01:thread2 dosoming1 ....
2016-9-28 15:13:01:スレッド1エンド。
2016-9-28 15:13:06:thread2 dosoming2 ....
2016-9-28 15:13:06:Thread2が終了しました。
2016-9-28 15:13:06:Thread2がアンロードされています。
2016-9-28 15:13:06:スレッド2の終わり。
Thread1が最初にロックを取得し、Thread2も後でロックを取得することがわかりますが、Thread1は現時点ではそれを占有しているため、Thread2はLockを解放するまでロックを取得しません。
**このコードは、ロックを取得するためにロック散布の背後にあるスレッドが、ロックを取得する前に前のロックをリリースするのを待つ必要があることを示しています。 **しかし、まだ中断可能な機能はないので、これにいくつかのコードが追加されます。
thread2.start(); try {thread.sleep(1000); } catch(arturnedexception e){e.printstacktrace();} // 1秒のスレッド2.inter2.interrupt();thread2が開始されたら、thread2の割り込み方法を呼び出します。 OK、最初にコードを実行して結果を確認してください。
2016-9-28 15:16:46:スレッド1ロックを取得します。
2016-9-28 15:16:46:Thread1がロックされています。
2016-9-28 15:16:46:Thread1 dosoming1 ....
2016-9-28 15:16:46:スレッド2ロックを取得します。
2016-9-28 15:16:47:スレッド2が中断されます。 < - スレッド割り込みに直接応答します
2016-9-28 15:16:51:Thread1 Dosoming2 ....
2016-9-28 15:16:51:Thread1が終了しました。
2016-9-28 15:16:51:スレッド1がアンロードされています。
2016-9-28 15:16:51:スレッド1エンド。
以前のコードと比較して、thread2がthread1がロックを放出するのを待っていることがわかりますが、thread2自体が中断し、thread2の背後にあるコードは引き続き実行されません。
readwritelock
名前が示すように、それは読み取りワイトロックです。この種のREAD-WRITEロックアプリケーションシナリオは、このように理解できます。たとえば、データの波は読み取りのために主に提供されており、書き込み操作は比較的少数しかありません。ミューテックスロックを使用すると、スレッド間のロック競争につながります。読んでいるときに誰もがそれを読むことができるなら、それが書かれたらリソースをロックしてください。このような変更はこの問題をうまく解決し、読み取り操作が書き込み操作に影響を与えることなく読み取りパフォーマンスを改善できるようにします。
複数の読者がリソースにアクセスしたり、1人のライターがアクセスしたりすることができ、両方を同時に実行することはできません。
これは、読み取りロックと書き込みロックを定義する読み取りおよび書き込みロックの抽象的なインターフェイスです。
public interface readwritelock { /***読みに使用されるロックを返します。 * * @returnの読み取りに使用されるロック */ lock readlock(); /***書き込みに使用されるロックを返します。 * * @Retturnの書き込みに使用されるロック */ lock writelock();}JDKにはReentrantreadWritelockの実装があります。 ReentrantreadWriteLockは、公正または不公平の2つのタイプに構築できます。建設中に明示的に指定されていない場合、デフォルトではフェア以外のロックが作成されます。フェア以外のロックモードでは、スレッドアクセスの順序が不確かです。つまり、分割することができます。ライターから読者に格下げすることはできますが、読者はライターにアップグレードすることはできません。
公正なロックモードの場合、オプションは最長の待機時間でスレッドに引き渡されます。読み取りスレッドがロックを取得し、書き込みスレッドが書き込みロックを要求した場合、書き込み操作が完了するまで読み取りロックの取得は受信されなくなります。
単純なコード分析は、実際にはReentranTreadWriteLockの同期ロックを維持していますが、セマンティックに読み取りロックと書き込みロックのように見えます。そのコンストラクターを見てください:
Public ReentrantreadWritelock(Boolean Fair){sync = fair? new fairsync():new non -fairsync(); readerlock = new ReadLock(this); writerlock = new writelock(this);} //読み取りロック保護されたreadlock(reintrantreadwritelock lock){sync = lock.sync;} //書き込みロック保護されたWritelock(Reentrantreadwritelockロック){sync = lock.sync;}の構築者//ReentrantreadWritelockの同期ロックオブジェクトが実際に構築されたときに参照されることがわかります。そして、この同期クラスは、ReentrantreadWriteLockの内部クラスです。要するに、読み取り/書き込みロックはすべて同期によって行われます。 2つの関係についてどのように協力しますか?
//ロックロックのロック方法public void lock(){sync.acquireshared(1);} // lock lock lock public void lock(){sync.acquire(1);}主な違いは、読み取りロックが共有ロックを取得し、書き込みロックが排他的ロックを取得することです。ここに言及できるポイントがあります。つまり、ReentrantreadWriteLockを確保するために、共有ロックと排他的なロックの両方が保持数とリエントラントの両方をサポートする必要があります。 ReentrantLockは状態を使用して保存され、状態は1つのシェーピング値のみを保存できます。 2つのロックの問題と互換性があるため、共有ロックを保持するスレッドの数またはそれぞれ排他的ロックまたは再入国郡を保持するスレッドの数に分割されます。
他の
私は書くのに時間がかかりすぎると感じた大きな記事を書きました、そして、もっと便利なロックがあります:
CountDownLatch
同時に保持されるカウンターを設定することです。発信者が待望のCountDownLatchの方法を呼び出すと、現在のカウンターが0でない場合にブロックされます。CountDownLatchのリリース方法を呼び出すと、待機する発信者がブロックを解除するまでカウントを減らすことができます。
セマフォン
セマフォは、100個のライセンスのセットアップなど、100個のスレッドが同時にロックを保持できるようにするなど、許可とライセンスの一形態であり、この金額が超えた場合、失敗に戻ります。
この記事を読んでくれてありがとう、私はそれがあなたを助けることができることを願っています。このサイトへのご支援ありがとうございます!