シーンスレッドセーフなソリューションを分散、分散ロックを達成するためのRedis

実用的な仕事は、多くの場合、同様の同時ここマルチスレッド時にスナップ遭遇し、この記述のマルチスレッドはロックの実装を分散Redisのシンプルなグラブ投票しています。


直接コードに。まずによって大会、与えられた誤差モデル:


何が起こるとき、20件のスレッドと10枚の切符を奪うとき私たちは、見ることができます。


パッケージcom.tiger.utils。

 

パブリッククラスTestMutilThread {

 

//票の合計額

公共の静的なint型の数= 10;

 

パブリック静的無効メイン(文字列[] args){

statrtMulti();

}

 

パブリック静的ボイドstatrtMulti(){

以下のために(INT I 1 =; I <= 20; I ++){

TicketRunnable tickrunner =新しいTicketRunnable();

スレッドスレッド=新しいスレッド(tickrunner、 "いいえスレッド:" + I)。

thread.start();

}

 

}

 

パブリック静的クラスTicketRunnable実装Runnableを{

 

@オーバーライド

ます。public void実行(){

System.out.println(にThread.currentThread()。のgetName()+ "スタート"

+カウント数)。

// TODO自動生成されたメソッドスタブ

// logger.info(にThread.currentThread()。のgetName()

// +)+ count "を実際に開始します"。

IF(カウント<= 0){

System.out.println(にThread.currentThread()。のgetName()

+「チケットは完売!ノーチケットが残りました!」+カウント数)。

リターン;

} そうしないと {

カウント=カウント - 1。

System.out.println(にThread.currentThread()。のgetName()

+ "今残り、切符を買っ:" +(カウント));

}

}

}

}

テスト結果は、結果から見ることができ、投票は別のスレッドで混乱しています。


ノースレッド:2が10を起動します

ノースレッド:6が10を起動します

ノースレッド:4の10を起動します

ノースレッド:5は10を起動します

いいえスレッド:3開始10

ノースレッド:9の開始を6

ノースレッド:1つの開始10

ノースレッド:1は今残り、切符を買っ:3

ノースレッド:9は今残り、切符を買っ:4

ノースレッド:3は今残り、切符を買っ:5

ノースレッド:12開始3

ノースレッド:5は今残り、切符を買っ:6

ノースレッド:4は今残り、切符を買っ:7

ノースレッド:8開始を7

ノースレッド:7開始を8

ノースレッド:12は今残り、切符を買っ:1

ノースレッド:14開始0

ノースレッド:6今残り、切符を買っ:8

ノースレッド:16開始0

ノースレッド:2は今残り、切符を買っ:9

ノースレッド:16チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:14チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:18開始0

ノースレッド:18チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:7今残り、切符を買っ:0

ノースレッド:15開始0

ノースレッド:8は今残り、切符を買っ:1

ノースレッド:13スタート2

ノースレッド:19開始0

ノースレッド:11開始3

ノースレッド:11チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:10開始3

ノースレッド:10チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:19チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:13チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:20開始0

ノースレッド:20チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:15チケットは完売!いいえチケットが残っていなかった!0

ノースレッド:17開始0

ノースレッド:17チケットは完売!いいえチケットが残っていなかった!0

複数のスレッド起こる混乱を解決するために、ここでは実際のテストクラスです!


本当のテストクラスは10枚の切符を奪う、ここでは20個のスレッドを起動します。


RedisTemplate Redisの操作は、統合されたスプリングにより、達成するために使用されます。ここでは、私がテストクラスに渡された外部RedisTemplateの形で構成されたので、RedisTemplateを使用することです。


MultiTestLockは、ロックされたツールを達成するために使用されます。


揮発性のキーワードを使用して投票の合計数は、より低い揮発性のキーワードの役割を理解するために指すことができ、システム・メモリ内の変数の可視性を、マルチスレッド。


アナログ機能のためのTicketRunnableグラブ票。


ロックの間に存在し、UNLO​​CK場合前記本明細書中に同期を保証するために、スレッドの安全性を確保するために、決定します。


テストカテゴリ:


パッケージcom.tiger.utils。

 

インポートしたjava.io.Serializable;

 

輸入org.slf4j.Logger;

輸入org.slf4j.LoggerFactory。

輸入org.springframework.data.redis.core.RedisTemplate;

 

 

パブリッククラスマルチ・{

ロガーロガー= LoggerFactory.getLogger(MultiTestLock.class)。

プライベートRedisTemplate <シリアライズ、シリアライズ> redisTemplate。

公共MultiTestLockロック。

//票の合計額

公共の揮発性の静的なint型のカウント= 10;

 

ます。public void statrtMulti(){

ロック=新しいMultiTestLock(redisTemplate)。

以下のために(INT I 1 =; I <= 20; I ++){

TicketRunnable tickrunner =新しいTicketRunnable();

スレッドスレッド=新しいスレッド(tickrunner、 "いいえスレッド:" + I)。

thread.start();

}

 

}

 

パブリッククラスTicketRunnable実装Runnableを{

 

@オーバーライド

ます。public void実行(){

logger.info(にThread.currentThread()。のgetName()+ "スタート"

+カウント数)。

// TODO自動生成されたメソッドスタブ

IF(カウント> 0){

// logger.info(にThread.currentThread()。のgetName()

// +)+ count "を実際に開始します"。

lock.lock();

同期(本){

IF(カウント<= 0){

logger.info(にThread.currentThread()。のgetName()

+「チケットは完売!ノーチケットが残りました!」+カウント数)。

施錠開錠();

リターン;

}そうしないと{

カウント=カウント1;

logger.info(にThread.currentThread()。のgetName()

+ "今残り、切符を買っ:" +(カウント));

}

}

施錠開錠();

}そうしないと{

logger.info(にThread.currentThread()。のgetName()

+「チケットは完売します!」+カウント数)。

}

}

}

 

公共RedisTemplate <シリアライズ、シリアライズ> getRedisTemplate(){

redisTemplateを返します。

}

 

公共のボイドsetRedisTemplate(

RedisTemplate <シリアライズ、シリアライズ> redisTemplate){

this.redisTemplate = redisTemplate。

}

 

公共のマルチ(RedisTemplate <シリアライズ、シリアライズ> redisTemplate){

素晴らしい();

this.redisTemplate = redisTemplate。

}

}

ロックツール:


私たちは、操作がアトミックプログラムの実行でなければならないとき、スレッドの安全性を確保するために、それを知っています。それ以降のバージョンRedisのセットキーが期限切れになり、同時に設定されたタイムアウトを使用することができます。


支払われるべき最後の通信翼のインタビューを考えて、インタビュアーが質問を:どのように重要な問題は、中に分散ロックで動作しているときに我々は成功したということですが、あなたはロック解除操作を実行すると、フォローアップ事業を完了し、デッドロックを防ぐためにロックを分散します失敗しました。分散ロックで結果が解除することはできません。デッドロックは、その後のロックは正常なことはできません。だから、ここでの目的は、ロック解除が失敗しても、ロック後に自動解除がまだタイムアウト後に配布されるように、タイムアウトが失敗したロック解除の出現を防ぐことにある期限切れになります。


コード内のコメントは、詳細には、基準として使用することができます。


パッケージcom.tiger.utils。

 

インポートしたjava.io.Serializable;

輸入java.util.Arrays。

輸入java.util.Collections。

輸入java.util.HashMapを;

インポートするjava.util.Iterator;

輸入はjava.util.List;

java.util.Randomの輸入;

java.util.concurrent.TimeUnitのインポート。

輸入java.util.concurrent.locks.Condition。

輸入java.util.concurrent.locks.Lock;

 

輸入javax.sound.midi.MidiDevice.Info;

 

輸入org.slf4j.Logger;

輸入org.slf4j.LoggerFactory。

輸入org.springframework.dao.DataAccessException。

輸入org.springframework.data.redis.core.RedisOperations。

輸入org.springframework.data.redis.core.RedisTemplate;

輸入org.springframework.data.redis.core.SessionCallback;

輸入org.springframework.data.redis.core.script.RedisScript;

 

 

パブリッククラスMultiTestLock実装ロック{

ロガーロガー= LoggerFactory.getLogger(MultiTestLock.class)。

プライベートRedisTemplate <シリアライズ、シリアライズ> redisTemplate。

公共MultiTestLock(RedisTemplate <シリアライズ、シリアライズ> redisTemplate){

素晴らしい();

this.redisTemplate = redisTemplate。

}

 

@オーバーライド

ます。public voidロック(){

//ここでは、スレッドがでてくることを余儀なくされた後、ロック操作が行わつかむためにwhileループを使用します。唯一の後続の操作を実行するためにキーをつかみます

一方、(TRUE){

IF(のtryLock()){

{試します

//目的は、シミュレート時間のかかる事業にあるスリープ500ミリ秒に、このスレッドは、事業の終了前の値セットは、単にタイムアウトを打っていることを確認し、

//実際の生産をバイアスすることができ、経験が必要です

Thread.sleep(500リットル)。

// logger.info(にThread.currentThread()のgetName()+ "目覚めまでの時間"。);

リターン;

}キャッチ(InterruptedExceptionある電子){

// TODO自動生成されたcatchブロック

e.printStackTrace();

}

}そうしないと{

{試します

//睡眠中の周期の周波数を削減しながら、ここでMSをランダムにオブジェクトを提供しました 

Thread.sleep(新しいランダム()nextInt(200)100。);

}キャッチ(InterruptedExceptionある電子){

// TODO自動生成されたcatchブロック

e.printStackTrace();

}

}

}

}

 

@オーバーライド

パブリックブールのtryLock(){

//ここでは、またtransactionSupportサポートトランザクション操作を選択することができます

SessionCallback <OBJECT> sessionCallback =新しいSessionCallback <OBJECT>(){

@オーバーライド

パブリックオブジェクト(RedisOperations操作)を実行

スローDataAccessException {

operations.multi();

operations.opsForValue()setIfAbsent( "秘密"、 "答え")。

//であり得る処理時間の実際の業務に基づくようにタイムアウトを設定し、経験的な値であります

operations.expire( "秘密"、500リットル、TimeUnit.MILLISECONDS)。

対象物体= operations.exec()。

オブジェクトを返します。

}

}。

// 2つの動作を実行し、それぞれ二つの操作の結果に対応する値配列[TRUE TRUE]が取得する、最初の時間べきで偽の値は、第1段階設定エラーを示します

一覧<ブール>結果=(一覧)redisTemplate.execute(sessionCallback)。

// logger.info(にThread.currentThread()のgetName()+ "トライロック" +結果。);

もし(真== result.get(0)|| "真" .equals(result.get(0)+ "")){

logger.info(にThread.currentThread()のgetName()+ "トライロック成功"。);

trueを返します。

}そうしないと{

falseを返します。

}

}

 

@オーバーライド

パブリックブールのtryLock(長いのarg0、arg1にするTimeUnit)

InterruptedExceptionが{スロー

// TODO自動生成されたメソッドスタブ

falseを返します。

}

 

@オーバーライド

公共ボイドロック解除(){

//ロック解除操作にタイムアウト期間を完了しない場合は、直接、ロックを外すには、後続のスレッドが継続するように、直接削除されます。行為は、ロックが有効期限が切れているか、削除されたことを確認するために、ナイフを作ります

SessionCallback <OBJECT> sessionCallback =新しいSessionCallback <OBJECT>(){

@オーバーライド

パブリックオブジェクト(RedisOperations操作)を実行

スローDataAccessException {

operations.multi();

operations.delete( "秘密");

対象物体= operations.exec()。

オブジェクトを返します。

}

}。

オブジェクトの結果はredisTemplate.execute(sessionCallback)を=。

}

 

 

@オーバーライド

ます。public void lockInterruptibly()例外:InterruptedExceptionをスロー{

// TODO自動生成されたメソッドスタブ

}

 

@オーバーライド

パブリック条件newCondition(){

// TODO自動生成されたメソッドスタブ

ヌルを返します。

}

公共RedisTemplate <シリアライズ、シリアライズ> getRedisTemplate(){

redisTemplateを返します。

}

 

公共のボイドsetRedisTemplate(

RedisTemplate <シリアライズ、シリアライズ> redisTemplate){

this.redisTemplate = redisTemplate。

}

 

}

結果




投票数が着実に減少し、フォローアップは、チケットをつかむことができずに、0に投票よりロック・スレッドをつかむなかった見ることができます。


チップ:


その間、問題、パッケージのマルチRedisのがありましたが、システムが与えられている:ERR EXEC MULTIなし


レビューは、問題の嘘ことを発見した後:


春に、MULTI繰り返し最初の実行ので、trueの場合、実行しない、毎回チェックこの変数を実行している真の、以降のコマンドにその内部変数セットisInMultiなり、コマンドは文句はありません実行しますコマンド。EXECコマンドが実行されます繰り返しエラーレポート「MULTIなしERR EXEC」の冒頭で述べました。


おすすめ

転載: blog.51cto.com/14661022/2464830