ワン:はじめに
この手順は主にデモのRedisを理解するために生産者 - 消費者モデルを理解するために使用し、これを通じてさ書くことはどのように達成し、どのような並行処理関連の知識を統合するためにされたアトミックタスクを取るためにシングルスレッドであり、これはデモではあるが、しかし、ほとんど変化の下でまた、プロジェクトのコンポーネントをログに記録するときのようにlog4j / logbackを想定する必要はありませんプロジェクトで使用することができるアペンダ部分。
2:実装
1. MQなどのLinkedListの使用は(もJDK LinkedBlockingQueueが付属していますが、このデモでは、より良いので、よりを書く基本原則を理解するために主にあります)。
2.消費者がリアルタイム/ログでMQオブジェクトからログを取得するようデーモンスレッドを使用して、スレッドプール、スレッドプールに提出し、すべてのアペンダを通って、その通知方法を呼び出して、この場所は、シーンに基づくことができます効率の最適化、代わりに、このスレッドプール非同期通知オブザーバに再提出される各アペンダアペンダ介して意志ループとして;
3.ログインタフェースとして生産ロギング生産するための方法を提供し、いずれかの個々のバッグの非同時使用するための生産または消費ログログオブジェクトキューオブジェクトは、操作キュー中にロックを必要とします。
4.消費者の取得する前に、まず、そこに取得し、スレッドプールに提示、または他に待っているそこMQデータかどうかを決定します。
生産者はそのためだけのnotifyAllの代わりに通知する必要がある5.通知し、消費者に通知を取得するには、ログオブジェクトを生成するために、生産者、消費者だけであるため、しかしを待つことはありません
6.残りの部分は、それを説明するためのコード次第です。
3:コード
1.MyLoggerクラスを実現
パッケージme.study.mqlogger.log。 インポートにjava.io.IOException; 輸入てjava.io.PrintWriter; インポートのjava.io.Writer; 輸入java.text.SimpleDateFormatの。 輸入java.utilの。*; 輸入java.util.concurrentの*。; 輸入java.util.concurrent.atomic.AtomicLong; 輸入java.util.concurrent.locks.Lock。 輸入java.util.concurrent.locks.ReentrantLock。 輸入静的me.silentdoer.mqlogger.log.MyLogger.LogLevel.DEBUG。 輸入静的me.silentdoer.mqlogger.log.MyLogger.LogLevel.ERROR。 / ** * @authorのwangsong * @version 1.0 * @descriptionここだけのシンプルなロガーを行い、機能にアペンダなどを提供していない、主にMQの生産者と消費者の原則を研究し、実装するために使用されて実現 * @date 9/26/19 18:07 * / パブリッククラスMyLogger { 民間のLogLevel loggerLevel = DEBUG; プライベート文字列のcharset =「UTF-8」; //当面は役に立たない、しかし、あなたは、シリアル化を使用する可能性がある必要がある場合。 // TODOは直接LinkedQueueを使用して、手動で時刻同時ReentrantLockのことで、データのセキュリティ(も同期)を実現することができます //プライベートのBlockingQueue <たLogRecord>キュー=新しいLinkedBlockingQueue <たLogRecord>(); //サポート同時LinkedListのように理解することができます // TODO底、それは単に学習の原理を実装することですので、一瞬のために少しより多くの思考 民間最終キュー<たLogRecord>レコードを=新しいLinkedListは<たLogRecord>(); 外部から取得したため、生産どのように多くのログレコードのための// TODO プライベートAtomicLong produceCount =新しいAtomicLong(0); どのように多くのログレコードの消費のために// TODO プライベートAtomicLong consumeCount =新しいAtomicLong(0); //消費者TODOロギング プライベートスレッド消費者=新しいLogDaemon(); パブリックMyLogger(){ consumer.setDaemon(真の); consumer.start(); } / ** *外部インタフェースを提供し、それは生産・ログ・データ・インタフェースのための生産者をログに記録することです * @Param MSG * @paramレベル * / 公共ボイドログ(文字列MSG、ログレベルレベル){ 日CURR = generateCurrDate(); ログイン(新規たLogRecord(レベル、MSG、CURR)); } / ** *外部インタフェースを提供し、それは生産・ログ・データ・インタフェースのための生産者をログに記録することです * @Param MSG * / 公共ボイドログ(文字列のMSG){ 日CURR = generateCurrDate(); ログ(新たLogRecord(this.loggerLevel、MSG、CURR)); } / ** *生産者(すなわち、コールログプロセスは、生産履歴オブジェクトにおけるプロデューサとして理解することができる)の製造ロギングのためのインタフェースを提供します * @paramレコード * / 公共ボイドログ(LogRecordのレコード){ // ReentrantLockのは同期しますが、十分である現在のシナリオで同期置き換えることができます LinkedBlockingQueueことをこれが必要とされていない場合(this.records){// TODO同期 this.records.offer(レコード)。 this.produceCount.incrementAndGet(); this.records.notify(); // TODOつのみのスレッドが十分)(通知したがって、)(records.waitう } } シングルスレッドのRedis、オブジェクトのリードコマンド、及びこれを対応する位置にデータを書き込むことにより、アペンダを読んでたLogRecordするために使用されるのと同様// TODO プライベートクラスLogDaemonは{スレッドを拡張します プライベート揮発ブール有効=はtrue。 //アペンダとして働きます プライベートリスト<ライター>アペンダ= NULL; プライベートExecutorServiceのスレッドプール=て、新しいThreadPoolExecutor(1、3 、180000、TimeUnit.MILLISECONDS、新しいLinkedBlockingQueue <Runnableを>(1024)); @オーバーライド ます。public void実行(){ 一方、(this.valid){ // TODOは、少なくともの原則に従って、このスレッドは、あなたがこのスレッドが外の世界に中断される可能性があると思うだろう全体的な割り込みで場所があるかどうかを考えることではないところを知っているので、何らかの処理を行う必要があります {試します 同期(MyLogger.this.records){ IF(MyLogger.this.records.size()<= 0){ MyLogger.this.records.wait(); } 最終たLogRecord firstRecord = MyLogger.this.records.poll()。 MyLogger.this.consumeCount.incrementAndGet(); //threadPool.submit() threadPool.execute(() - > MyLogger.this.notifyAppender(this.appenders、firstRecord))。 } }キャッチ(InterruptedExceptionあるのEX){ this.valid = falseは、 ex.printStackTrace(); }キャッチ(ThrowableをT){ t.printStackTrace(); } } } } プライベートボイドnotifyAppender(最終一覧<ライター>アペンダ、最終的なLogRecordレコード){ IF(アペンダ== NULL){ PrintWriterライター=新しいPrintWriter(?record.level == ERRORをSystem.err:のSystem.out); writer.append(record.toString())。 writer.flush(); }他{ それは非同期にアペンダRunnableオブジェクトラッパーのそれぞれによって実行され、その後、スレッドプール(またはミドルウェアの中央)に提出することが可能である場合、// TODOこれは、同期しています {:(アペンダライターライター)について {試します writer.append(record.toString())。 }キャッチ(のIOExceptionのEX){ ex.printStackTrace(); } } } } / ** *実際の状況のタイムスタンプのルートにLogRecordが一致同時結果を防ぐためとして、現在の時刻を生成するための手段 * / プライベートロックcurrDateLock =新しいReentrantLockの(); //もと直接同期させることができ プライベート日付generateCurrDate(){ currDateLock.lock(); 日付結果=新しいDate(); currDateLock.unlock(); 結果を返します。 } //プロデューサーのデータオブジェクトを生成します パブリック静的クラスLogRecordの{ 民間のLogLevelレベル。 プライベート文字列MSG。 プライベート日のタイムスタンプ。 プライベート静的最終のSimpleDateFormat DEFAULT_DATE_FORMAT =新しいてSimpleDateFormat( "YYYY-MM-DD HH:MM:SS:SSS"); 民間のSimpleDateFormat DATEFORMAT = DEFAULT_DATE_FORMAT。 / *パブリックたLogRecord(){ この(INFO、 ""); } * / パブリックたLogRecord(ログレベルのレベル、文字列MSG){ この(レベル、MSG、新しいDate()); //は、そうでない場合は、高同時実行の下で許可されていない外の世界からタイムスタンプまたは最高は、より多くのだろう設定 } // TODO最高は不正確または他の並行性の高いタイムスタンプの下で発生しやすいのこの順序を使用します。 パブリックたLogRecord(ログレベルのレベル、文字列MSG、日付スタンプ){ this.level =レベル。 this.msg = MSG; this.timestamp =タイムスタンプ。 } @オーバーライド パブリック文字列のtoString(){ String.Formatの( "[レベル:%sは、日時:%S]:S%を\ n"、レベル、dateFormat.format(タイムスタンプ)、MSG)を返します。 } 公共のLogLevelも、getLevel(){ レベルを返します。 } パブリック文字列GETMSG(){ MSGを返します。 } 公共ボイドsetDateFormat(SimpleDateFormatのDATEFORMAT){ this.dateFormat = DATEFORMAT。 } ます。public void setTimestamp(日付スタンプ){ this.timestamp =タイムスタンプ。 } } パブリック列挙型のLogLevel {// TODO列挙内部のデフォルトは静的です INFO、 デバッグ、 エラー } 公共のLogLevel getLoggerLevel(){ loggerLevelを返します。 } 公共ボイドsetLoggerLevel(ログレベルloggerLevel){ this.loggerLevel = loggerLevel。 } パブリック文字列getCharset(){ 文字セットを返します。 } ます。public void setCharset(文字列の文字セット){ this.charset =文字セット。 } 公共AtomicLong getProduceCount(){ produceCountを返します。 } 公共AtomicLong getConsumeCount(){ consumeCountを返します。 } }
2.テストケース1
パッケージme.study.mqlogger。 輸入me.silentdoer.mqlogger.log.MyLogger。 インポートjava.util.Scanner; / ** * @authorのwangsong * @version 1.0 * @description説明 * @date 9/26/19午前10時13分PM * / パブリッククラスエントランス{ プライベート静的MyLoggerロガー=新しいMyLogger(); パブリック静的無効メイン(文字列[] args){ //logger.setLoggerLevel(MyLogger.LogLevel.ERROR); スキャナスキャナ=新しいスキャナ(System.in)。 文字列のライン。 一方、(!(ライン= scanner.nextLine())。等号( "出口")){ もし(line.equals( "")) 持続する; logger.log(ライン)。 System.out.println(String.Formatの( "ログの%の総生産"、Logger.getConsumeCount()))。 {試します Thread.sleep(500)。 }キャッチ(InterruptedExceptionあるのEX){} System.out.println(String.Formatの( "ログの%の総消費量"、Logger.getProduceCount()))。 } } }
3.テストケース2
パッケージme.study.mqlogger。 輸入me.silentdoer.mqlogger.log.MyLogger。 輸入java.util.concurrent.ExecutorService。 輸入java.util.concurrent.Executors。 / ** * @authorのwangsong * @version 1.0 * @description説明 * @date 9/26/19 22:32 * / パブリッククラスEntrance2 { プライベート静的MyLoggerロガー=新しいMyLogger(); パブリック静的無効メイン(文字列[] args){ logger.setLoggerLevel(MyLogger.LogLevel.ERROR)。 ExecutorServiceのスレッドプール= Executors.newCachedThreadPool()。 {(; iが10 <I ++はiが0 = INT)のために 最終int型のインデックス= I + 1; threadPool.execute(() - > { logger.log(String.Formatの( "生産レコードの%sの。"、インデックス))。 System.out.println(String.Formatの( "%sの総生産記録。"、インデックス)); }); {試します Thread.sleep(100)。 }キャッチ(InterruptedExceptionあるのEX){} } {試します Thread.sleep(3000); System.out.println(String.Formatの( "%sのレコード総消費量である。"、Logger.getConsumeCount()))。 }キャッチ(InterruptedExceptionあるのEX){} //threadPool.shutdown()。 //threadPool.shutdownNow()。 } }
4:サプリメント
あなたが達成したい場合は、MQ要素のBlockingQueueの範囲と同一の数を制御することが可能であるMQ要素はの説明については、上限に達しているかどうかの状況を記述するために、2つのロック条件オブジェクトを作成することによって、すなわち、Confition ReentrantLockのことによって達成することができますMQは、要素の下限まで低下しました。
これは、上限または下限のいずれかが要素のプロセス制御性MQ番号を達成するために、生産/消費の対応する条件を通って物体の対応するブロックの生産または消費者に還元される達しました。