Tomcatが同時リクエストを処理する方法を理解することで、スレッドプール、ロック、キュー、および安全でないクラスについて理解することができます。次のメインコードは java-jre: sun.misc.安全でない java.util.concurrent.ThreadPoolExecutor java.util.concurrent.ThreadPoolExecutor.ワーカー java.util.concurrent.locks.AbstractQueuedSynchronizer java.util.concurrent.locks.AbstractQueuedLongSynchronizer java.util.concurrent.LinkedBlockingQueue トムキャット: org.apache.tomcat.util.net.Nioエンドポイント org.apache.tomcat.util.threads.ThreadPoolExecutor org.apache.tomcat.util.threads.タスクスレッドファクトリー org.apache.tomcat.util.threads.タスクキュー スレッドプールエグゼキュータスレッドを管理し、スレッドのオーバーヘッドを削減するスレッドプール実装クラスです。タスクの実行効率を向上させるために使用できます。 コンストラクタのパラメータは パブリックスレッドプールエグゼキュータ( int コアプールサイズ、 int 最大プールサイズ、 長いkeepAliveTime、 TimeUnit 単位、 BlockingQueue<Runnable> ワークキュー、 スレッドファクトリー スレッドファクトリー、 RejectedExecutionHandler ハンドラー) { } corePoolSizeはコアスレッドの数です Tomcat の http リクエストにおける ThreadPoolExecutor の適用このスレッドプールは、Tomcatがリモートリクエストを受け取った後、各リクエストを個別のタスクとして処理するために使用されます。execute(Runnable)が呼び出されるたびに 初期化
NioEndpointが初期化されると、スレッドプールが作成される。 パブリック void createExecutor() { 内部エグゼキュータ = true; タスクキュー taskqueue = 新しいタスクキュー(); //TaskQueue は無制限のキューであり、いつでも追加できるため、ハンドラーは無効な TaskThreadFactory と同等です。tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority()); executor = 新しい ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf); taskqueue.setParent( (ThreadPoolExecutor) executor); } スレッドプールが作成されたら、prestartAllCoreThreads()を呼び出してコアワーカースレッドを初期化し、 パブリック int prestartAllCoreThreads() { 整数n = 0; (addWorker(null, true)) の間 ++n; n を返します。 } addWorker の数が corePoolSize と等しい場合、addWorker(null,true) は false を返し、ワーカー スレッドの作成を停止します。 タスクをキューに送信する クライアントがリクエスト (http) するたびに、処理タスクが送信されます。 ワーカーはキューからタスクを取得して実行します。以下は、タスクをキューに入れるロジック コードです。 ThreadPoolExecutor.execute(Runnable) はタスクを送信します。 パブリック void execute(実行可能なコマンド) { if (コマンド == null) 新しい NullPointerException() をスローします。 ctl.get(); を呼び出します。 // ワーカーの数はコア スレッドの数より少ないですか? Tomcat での初期化後、通常は最初の条件は満たされず、addWorker は呼び出されません。 ワーカー数(c) < コアプールサイズの場合 if (addWorker(コマンド、true)) 戻る; ctl.get() 関数は、次のコードで使用できます。 } // workQueue.offer(command) タスクをキューに追加します。 if (isRunning(c) && workQueue.offer(command)) { int 再チェック = ctl.get(); if (!isRunning(再確認) && remove(コマンド)) 拒否(コマンド); そうでない場合 (workerCountOf(recheck) == 0) ワーカーを追加します(null、false); } そうでない場合 (!addWorker(command, false)) 拒否(コマンド); } workQueue.offer(command) はタスクの送信を完了します (Tomcat がリモート http 要求を処理するとき)。 ワークキューオファー TaskQueue は BlockingQueue の具体的な実装クラスであり、workQueue.offer(command) の実際のコードは次のとおりです。 パブリックブールオファー(E e) { e == null の場合、新しい NullPointerException() をスローします。 最終的な AtomicInteger count = this.count; count.get() == 容量の場合 false を返します。 c = -1; ノード<E> ノード = 新しいノード<E>(e); 最終的な ReentrantLock putLock = this.putLock; ロックを置きます。 試す { count.get() が容量未満の場合 enqueue(node); //ここでキューにタスクを追加します c = count.getAndIncrement(); (c + 1 < 容量)の場合 シグナルが満杯でない。 } ついに ロックを解除します。 } (c == 0)の場合 シグナルが空でない(); c >= 0 を返します。 } // タスクをキューに追加します/** * キューの最後にノードをリンクします。 * * @param node ノード */ プライベートvoidエンキュー(Node<E>ノード) { // putLock.isHeldByCurrentThread() をアサートします。 // last.next == null であることを確認します。 last = last.next = node; //リンクリスト構造last.next = node; last = node } 後はワーカーの仕事です。run メソッドではワーカーが getTask() を呼び出してここで送信されたタスクを取得し、実行します。 スレッド プールは新しく送信されたタスクをどのように処理しますか? ワーカーを追加したら、タスクを送信します。ワーカーの数が corePoolSize に達すると、タスクはキューに入れられ、ワーカーの run メソッドがループしてキュー内のタスクを取得します (キューが空でない場合)。 ワーカー実行方法: /** メイン実行ループを外側の runWorker に委譲します */ パブリックボイド実行() { 実行ワーカー(これを); } キュー内のタスクをループする RunWorker(worker) メソッドのループ コード: 最終的なvoid runWorker(ワーカーw) { スレッド wt = Thread.currentThread(); 実行可能なタスク = w.firstTask; w.firstTask = null; w.unlock(); // 割り込みを許可する ブール値completedAbruptly = true; 試す { while (task != null || (task = getTask()) != null) { // キュー内のタスクを取得するためにループします w.lock(); // ロックします try { //実行前処理 beforeExecute(wt, task); //キュー内のタスクはtask.run()の実行を開始します。 // 後処理を実行します afterExecute(task, throw); ついに タスク = null; 完了したタスク++; w.unlock(); // ロックを解除する} } 突然完了 = false; ついに processWorkerExit(w, 突然完了); } } task.run()はタスクを実行する ロックアプリケーション ThreadPoolExecutor はロックを使用して次の 2 つのことを保証します。 キューにタスクロックを追加する パブリックブールオファー(E e) { e == null の場合、新しい NullPointerException() をスローします。 最終的な AtomicInteger count = this.count; count.get() == 容量の場合 false を返します。 c = -1; ノード<E> ノード = 新しいノード<E>(e); 最終的な ReentrantLock putLock = this.putLock; putLock.lock(); //ロックしてみる{ count.get() が容量未満の場合 エンキュー(ノード); カウントの取得と増分(); (c + 1 < 容量)の場合 シグナルが満杯でない。 } ついに putLock.unlock(); //ロックを解除する} (c == 0)の場合 シグナルが空でない(); c >= 0 を返します。 } キュータスクロックを取得 プライベート実行可能getTask() { boolean timedOut = false; // 最後の poll() はタイムアウトしましたか? // ...省略 for (;;) { 試す { 実行可能 r = 時間制限あり ? workQueue.poll(keepAliveTime、時間単位.NANOSECONDS): workQueue.take(); //キュー内のタスクを取得する if (r != null) r を返します。 タイムアウト = true; } catch (InterruptedException 再試行) { タイムアウト = false; } } } パブリックE take()はInterruptedExceptionをスローします{ 元; c = -1; 最終的な AtomicInteger count = this.count; 最終的な ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); // ロックを試みる { count.get() == 0 の間 notEmpty.await(); //キューにタスクがない場合は待機します} x = デキュー(); c = count.getAndDecrement(); (c > 1)の場合 空ではないシグナル(); ついに takeLock.unlock(); // ロックを解除する} (c == 容量)の場合 シグナルが満杯でない(); x を返します。 } 不安定な 並行シナリオでは、このキーワードはメンバー変数を変更するために非常によく使用されます。 主な目的は、パブリック変数が1つのスレッドによって変更されると、他のスレッドから見えるようにすることです(リアルタイム) sun.misc.安全でない高並行性関連クラス スレッド プールを使用する場合、Unsafe クラスがよく使用されます。このクラスは、いくつかのアトミック CAS 操作、スレッドのロック、スレッドの解放などを高い並行性で実行できます。 アトミックデータ操作java.util.concurrent.locks.AbstractQueuedSynchronizerクラスには、アトミック操作を保証するコードがあります。 保護された最終的なブール値の compareAndSetState(int expect, int update) { // これをサポートするための組み込み関数の設定については以下を参照してください unsafe.compareAndSwapInt(this、stateOffset、expect、update) を返します。 } Unsafe クラスに対応するコード: //対応するJavaの最下層は実際にはネイティブメソッドであり、C++コードに対応しています/** * Java変数が現在<tt>x</tt>である場合、アトミックに更新します。 * <tt>期待どおり</tt>を保持します。 * 成功した場合は @return <tt>true</tt> */ パブリックファイナルネイティブブールcompareAndSwapInt(Object o, long offset, int が期待される、 整数x); メソッドの機能は、アトミック操作を保証するために値を更新するだけです。オブジェクト つまり、期待値がメモリ内の値と同じ場合、つまり期待値 == メモリ内の値の場合、更新された値は x となり、変更が成功したことを示す true が返されます。 それ以外の場合、期待値はメモリ値と異なり、値が他のスレッドによって変更されており、x に更新できないことを示します。アトミック変更が失敗したことをオペレーターに通知するために、False が返されます。 スレッドのブロックと起動public native void park(boolean isAbsolute, long time); //現在のスレッドをブロックする スレッド プールのワーカー ロールは、キュー タスクを取得するためにループします。キューにタスクがない場合、worker.run は待機中のままで、スレッドは終了しません。コードでは、 最下層は unsafe.unpark() はスレッドを起動します この操作は対応しています。ブロックされている場合は、まずスレッドをキューに入れます。起動されている場合は、ブロックされているスレッドをキューから取り出し、unsafe.unpark(thread) で指定されたスレッドを起動します。 リンクリストを通じてスレッド情報を保存する // ブロッキングスレッドを追加する private Node addConditionWaiter() { ノード t = lastWaiter; // lastWaiter がキャンセルされた場合は、クリーンアップします。 if (t != null && t.waitStatus != Node.CONDITION) { リンク解除CancelledWaiters(); t = 最後のウェイター; } ノード node = new Node(Thread.currentThread(), Node.CONDITION); t == nullの場合 firstWaiter = ノード; それ以外 t.nextWaiter = ノード; lastWaiter = node; // 新しくブロックされたスレッドをリンク リストの最後に配置します。 return node; } // ブロックされたスレッドを取り出す public final void signal() { if (!isHeldExclusively()) 新しい IllegalMonitorStateException() をスローします。 Node first = firstWaiter; //リンクリスト内の最初のブロックされたスレッド if (first != null) doSignal(最初に); } //取得後、このスレッドを起動します final boolean transferForSignal(Node node) { LockSupport.unpark(node.thread); true を返します。 } パブリック静的void unpark(スレッドスレッド) { (スレッド!= null)の場合 UNSAFE.unpark(スレッド); } Tomcat がスレッド プールを使用してリモート同時要求を処理する方法について説明したこの記事はこれで終わりです。Tomcat スレッド プールによるリモート同時要求の処理の詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: mysql と oracle のデフォルトのトランザクション分離レベルの説明
>>: HTML テーブル マークアップ チュートリアル (37): 背景画像属性 BACKGROUND
目次01 InnoDBレプリカセットの紹介02 InnoDBレプリカセットの制限03 導入前に知って...
HTML は、Baidu 百科事典のナビゲーション ドロップダウン メニュー機能を模倣します。具体的...
1. はじめに最近、 Webpackの原理を勉強しています。これまでは Webpack の設定方法し...
1. 親divは疑似クラスafterとzoomを定義します <スタイル タイプ="...
1. ウィンドウ -> 設定を選択してEclipseの設定パネルを開きます。 2. 「設定」ウ...
<br />2 年前に PPK が投稿した素晴らしいブログ記事では、contains()...
解決策1完全にアンインストールしてすべてのデータを削除します。まず、MySQLに関連するすべてのプロ...
目次1. テストデータ2. ヌル値による不便3. スペース、空の値、null をどのように判断すれば...
目次非同期とは何ですか?なぜ非同期性が必要なのでしょうか?非同期IOとは何ですか?イベントループとは...
CSS の優先順位について話す前に、CSS とは何か、CSS が何に使用されるのかを理解する必要があ...
この記事では、クリックして切り替える認証コードと認証を実装するためのJavaScriptの具体的なコ...
1. 準備Linux オペレーティング システムをインストールした後、ここで Linux 7 を選択...
遅延読み込み(レイジー読み込み)とプリロードは、Web 最適化によく使用される手段です。 。 1. ...
この記事では、シンプルなショッピングカートを実装するためのJavaScriptの具体的なコードを参考...
目次NIS の紹介ネットワーク環境: 1. 環境の準備(両方のノードが必要) 2.nisマスターサー...