Semaphoreは、JUCパッケージで一般的に使用されるクラスです。 AQS共有モードのアプリケーションです。複数のスレッドを同時に共有リソースで動作させることができ、同時性の数を効果的に制御できます。それは良い交通制御を達成することができます。 Semaphoreは、バスのチケットと見なすことができるライセンスの概念を提供します。チケットを正常に入手できる人だけがバスに乗ることができます。一定の数のチケットがあり、制限なしにそれらを発行することは不可能であり、バスの過負荷につながります。そのため、チケットが発行された場合(バスがいっぱいです)、他の人は次の列車を待つことしかできません。誰かが途中でバスを降りると、彼のポジションは無料になります。そのため、他の人がこの時点でバスに乗りたい場合は、チケットを再び入手できます。セマフォを使用してさまざまなプールを実装できます。この記事の最後に、簡単なデータベース接続プールを書きます。まず、セマフォのコンストラクターを見てみましょう。
// Constructor 1Public Semaphore(int Permits){sync = new non -fairsync(permits);} // constructor 2public semaphore(int permits、boolean fair){sync = fair? new FairSync(許可):new nonfairsync(許可);}Semaphoreは2つのパラメーターのないコンストラクターを提供しますが、パラメーターのないコンストラクターは提供されていません。両方のコンストラクターは、初期のライセンス数で合格する必要があります。コンストラクター1を使用して構築されたセマフォは、ライセンスを取得するときに非フェアの方法で取得されます。 Constructor 2を使用すると、パラメーターを介してライセンスを取得する方法を指定できます(公正または不公平)。セマフォは、主に外部の世界に2種類のAPIを提供し、ライセンスを取得し、ライセンスをリリースします。デフォルトは1つのライセンスを取得およびリリースすることであり、パラメーターを渡して複数のライセンスを同時に取得およびリリースすることもできます。この記事では、毎回1つのライセンスを取得してリリースする状況についてのみ説明します。
1.ライセンスを取得します
//ライセンスの取得(応答割り込み)public void acchire()throws arturnedexception {sync.acquireshared upthuldibly(1);} //ライセンスを取得する(割り込みに応答しない)public void aun -interrumdibly(){sync.acquireshared(1);} sync.nonfairtryacquireshared(1)> = 0;} //ライセンスを取得しようとします(長いタイムアウト、タイムナイトユニット)arturnedexception {return sync.tryacquiresharednanos(1、unit.tonanos(タイムアウト));}上記のAPIは、セマフォが提供するデフォルトのライセンス取得操作です。一度に1つのライセンスのみを取得することも、実際の生活で一般的な状況です。直接フェッチに加えて、フェッチの試みも提供します。直接フェッチ操作は、障害後にスレッドをブロックする場合がありますが、フェッチを試みてもそうではありません。また、TryAcquireメソッドを使用して、不公平な方法でそれを取得しようとすることに注意する必要があります。通常の時間によく使用するのは、ライセンスを取得することです。それがどのように得られるかを見てみましょう。獲得メソッドは、直接sync.acquiresharedの途中で呼び出すことがわかります(1)。この方法は、AQSのメソッドです。 AQSソースコードシリーズの記事について話しました。もう一度レビューしましょう。
//中断モードでロックを取得する(共有モード)パブリックファイナルvoid accock acquireshared interintibly(int arg)arturnedexception {//最初にスレッドが中断されるかどうかを判断します。 } // 1。ロックを取得してみてください(tryacquireshared(arg)<0){// 2。取得が失敗した場合、doacquireshared utteribly(arg)を入力します。 }}最初のメソッドは、途中で途切れにくい方法です。TryAcquiresharedメソッドを呼び出して取得しようとします。 TryAcquiresharedは、AQSの抽象的な方法です。 2つの派生クラスFairSyncとNonFairSyncは、このメソッドのロジックを実装します。 FairSyncは公正な獲得の論理を実装し、非フェアシンは非フェア獲得の論理を実装します。
要約静的クラスの同期は、abstractqueeuedsynchronizer {// final int non -fairtryacquireshared(int quickire){for(;;){//利用可能なライセンスを取得できる= getState()を取得しようとします。 //残りのライセンスを取得してください// 1。 0未満の場合は、直接残ります// 2。 0を超える場合の場合、同期ステータスを最初に更新してから、残りのif(<0 || compareandsetState(利用可能、残り))を返します{return return; }}}} // nonfairsync static final class non -fairsync extends sync {private static final long serialversionuid = -2694183684443567898l; nonfairsync(int permits){super(permits); } //ライセンス保護されたint tryacquireshared(int quickires)を取得してみてください{return non -fairtryacquireshared(取得); }} // fair synchronizer static final class finalsync extends sync {private static final long serialversionuid = 2014338818796000944l; fairsync(int permits){super(permits); } //ライセンス保護されたint tryacquireshared(int quickires){ /for(;;){//同期キューの前に誰かがいるかどうかを判断してください。 } //利用可能なライセンスを取得できますintavail = getState(); //残りのライセンスを取得しますint残り=利用可能 - 取得; // 1。 0未満の場合は、残り// 2に直接戻ります。残りが0を超える場合、同期ステータスが最初に更新され、次に残りのif(<0 || compareandsetState(利用可能)){return return; }}}}ここでは、非授乳のtryAcquiresharedメソッドは、親クラスの同期にある非fairtryacquiresharedメソッドを直接呼び出すことに注意する必要があります。非フェア取得ロックのロジックは、最初に現在の同期状態(同期状態がライセンスの数を表す)を取り出し、現在の同期状態のパラメーターを差し引くことです。結果が0以上である場合、利用可能なライセンスがまだあることが証明され、同期状態の値はCAS操作を使用して直接更新され、最後に、結果値が0未満であるかどうかに関係なく返されます。負の数を返すことは、取得が失敗したことを意味し、ゼロは現在のスレッドが正常に取得されることを意味しますが、後続のスレッドを取得できなくなり、正の数は現在のスレッドが正常に取得され、後続のスレッドも取得できます。 AcquiiReshared -Fructibly Methodのコードを見てみましょう。
//中断モードでロックを取得する(共有モード)パブリックファイナルvoid accock acquireshared interintibly(int arg)arturnedexception {//最初にスレッドが中断されるかどうかを判断します。 } // 1。ロックを取得しようとしてください取得が失敗した場合、doacquireshared utteribly(arg)を入力します。 }}返された残りが0未満の場合、それは取得が失敗したことを意味します。したがって、tryacquireshared(arg)<0が真であるため、次にdoacquiresharedの途切れやすいメソッドが呼び出されます。 AQについて話すと、現在のスレッドをノードに包み、同期キューのテールに入れ、スレッドを一時停止することができます。これは、0未満のままである場合にスレッドがキューアップしてブロックする理由でもあります。返された残りの> = 0が現在のスレッドが正常に取得されたことを意味します。したがって、tryacquireshared(arg)<0はフレーズであるため、現在のスレッドをブロックするためにdoacquireshared -autructiblyメソッドはもはや呼び出されません。上記は、不公平な獲得の論理全体です。公正な買収の場合、これの前にhasqueuedprededexerorsメソッドを呼び出して、誰かが同期キューでキューインしているかどうかを判断する必要があります。その場合、return -1は取得が失敗したことを直接示します。そうしないと、次の手順は不公平な獲得として継続されます。
2。ライセンスをリリースします
//ライセンスのリリースpublic void release(){sync.releaseshared(1);}リリース方法を呼び出すことは、ライセンスをリリースすることです。その操作は非常にシンプルなので、AQのリリースメソッドを呼び出します。この方法を見てみましょう。
//ロック操作をリリースします(共有モード)パブリックファイナルブールリリースシャード(int arg){// 1。ロックをリリースしてみてくださいif(tryreleaseshared(arg)){// 2。リリースが成功した場合は、他のスレッドを覚ます(); trueを返します。 } falseを返します;}AQのリリースメソッドは、最初にTryReleaseSharedメソッドを呼び出して、ロックを解放しようとします。このメソッドの実装ロジックは、サブクラス同期にあります。
要約静的クラスの同期は、AbstractQueeuedsynchronizer {... //操作保護された最終的なブール奏者tryReleaseShared(int leleases)をリリースしようとします(;;){//現在の同期状態int current = getState(); //次のように現在の同期状態を次のようにint next = current +リリース。 //追加の結果が現在の同期状態よりも少ない場合、(次の<current){throw newエラー( "最大許可カウントを超えた")の場合、エラーが報告されます。 } // CASモードで同期状態の値を更新し、更新が成功した場合はtrueを返します。 }}} ...}TryReleaseSharedメソッドがforループを使用してスピンすることがわかります。まず、同期状態を取得し、着信パラメーターを追加してから、CASの同期状態を更新します。更新が成功した場合は、trueを返し、メソッドから飛び出します。それ以外の場合、ループは成功するまで続きます。これは、セマフォがライセンスをリリースするプロセスです。
3.接続プールを手動で書き込みます
セマフォコードはそれほど複雑ではありません。一般的に使用される操作は、ライセンスを取得してリリースすることです。これらの操作の実装ロジックは比較的簡単ですが、これはセマフォの広範な適用を妨げません。次に、Semaphoreを使用して、簡単なデータベース接続プールを実装します。この例を通じて、読者がセマフォの使用をより深く理解できることを願っています。
パブリッククラスConnectPool {//接続プールサイズプライベートINTサイズ。 //データベース接続コレクションPrivate Connect [] Connects; //接続ステータスフラグPrivate Boolean [] ConnectFlag; //利用可能な接続の残り数プライベート揮発性int利用可能。 //セマフォのプライベートセマフォセマフォ; // constructor public connectpool(int size){this.size = size; this.available = size; Semaphore = new Semaphore(size、true); connects = new Connect [size]; connectflag = new Boolean [size]; initconnects(); } //接続の初期化private void initconnects(){//指定された数のデータベース接続を生成します(int i = 0; i <this.size; i ++){connects [i] = new connect(); }} // get database connection private synchronized connect getConnect(){for(int i = 0; i <connectflag.length; i ++){//コレクションを転送して未使用の接続を見つけるif(!connectflag [i]){// connectflag [i] = true; //利用可能な接続の数を減算します - ; system.out.println( "【"+thread.currentthread()。getName()+"sementionの残りの数を取得するには:"+利用可能); //接続リファレンスを返すreturn connects [i]; }} nullを返します。 } //接続を取得しますpublic connect connect openconnect()throws arturnedexception {//ライセンスSemaphore.acquire(); //データベース接続を取得しますreturen getConnect(); } //接続をリリースしますpublic synchronized void release(connect connect){for(int i = 0; i <this.size; i ++){if(connect == connects [i]){//接続を使用しないconnectflag [i] = false; //使用可能な接続番号を1つ追加します。 system.out.println( "【"+thread.currentThread()。getName()+"]残りの接続番号をリリースします:"+availain); //ライセンスSemaphore.Release(); }}} //利用可能な接続の数を追加public int(){return avainal; }}テストコード:
public class testthread extends thread {private static connectpool = new ConnectPool(3); @Override public void run(){try {connect connect = pool.openconnect(); thread.sleep(100); //休憩を取るpool.release(connect); } catch(arturnedexception e){e.printstacktrace(); }} public static void main(string [] args){for(int i = 0; i <10; i ++){new testthread()。start(); }}}テスト結果:
配列を使用して、データベース接続への参照を保存します。接続プールを初期化するときは、initConnectsメソッドを呼び出して、指定された数のデータベース接続を作成し、その参照を配列に保存します。さらに、接続が利用可能かどうかを記録するために、同じサイズの配列があります。外部スレッドが接続の取得を要求するときはいつでも、最初にSemaphore.Acquire()メソッドを呼び出してライセンスを取得し、使用中に接続ステータスを設定し、最後に接続への参照を返します。ライセンスの数は、建設中に渡されたパラメーターによって決定されます。ライセンスの数は、Semaphore.acquire()メソッドが呼び出されるたびに1削減されます。数が0に縮小されると、使用可能な接続がないことを意味します。この時点で、他のスレッドが再びそれを取得すると、ブロックされます。スレッドが接続をリリースするたびに、semaphore.release()が呼び出され、ライセンスをリリースします。現時点では、ライセンスの総数が再び増加します。つまり、利用可能な接続の数が増加しています。次に、以前にブロックされたスレッドが目覚め、接続を取得し続けます。この時点で、再びそれを取得することで接続を正常に取得できます。テストの例では、3つの接続の接続プールが初期化されます。テスト結果から、スレッドが接続を取得するたびに、残りの接続の数が1削減されることがわかります。スレッドが0に減少すると、他のスレッドはそれを取得できなくなります。この時点で、それを取得し続ける前に、スレッドが接続をリリースするのを待つ必要があります。残りの接続の数は常に0〜3の間で変化することがわかります。つまり、テストが成功したことを意味します。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。