JDKネイティブスレッドプールのバグを修正するTomcatの実装原理

JDKネイティブスレッドプールのバグを修正するTomcatの実装原理

処理能力と同時実行性を向上させるために、Web コンテナは通常、リクエストを処理するタスクをスレッド プールに配置します。JDK のネイティブ スレッド プールは、CPU を集中的に使用するタスクに本質的に適しているため、Tomcat はこれを変更しました。

Tomcat スレッドプールの原則

実際、ThreadPoolExecutor のパラメータには主に次の重要なポイントがあります。

スレッド数を制限する

キューの長さを制限する

Tomcat はこれら 2 つのリソースを制限する必要があります。そうしないと、同時実行性が高い場合に CPU とメモリが枯渇する可能性があります。
したがって、Tomcat のスレッド プールは次のパラメータを渡します。

// カスタマイズされたタスク キュー taskqueue = new TaskQueue(maxQueueSize);

// カスタマイズされたスレッドファクトリー TaskThreadFactory tf = new TaskThreadFactory(namePrefix,
							                 デーモン、
							                 スレッド優先度を取得します。
);

// カスタム スレッド プール executor = new ThreadPoolExecutor(getMinSpareThreads(),
								  getMaxThreads()、
				 			      最大アイドル時間、 
				 			      時間単位.ミリ秒、
				 			      タスクキュー、
				 			      tf);

Tomcat にはスレッド数制限もあり、次のように設定します。

  • コアスレッド数 (minSpareThreads)
  • スレッドプールの最大数 (maxThreads)

Tomcat スレッド プールには独自の特別なタスク処理フローもあり、execute メソッドを書き換えることで独自の特別なタスク処理ロジックを実装します。

  1. corePoolSize タスクがある場合、タスクごとに新しいスレッドが作成されます。
  2. さらにタスクがある場合は、それらをタスク キューに入れて、すべてのスレッドが取得できるようにします。キューがいっぱいの場合は、一時的なスレッドを作成します
  3. スレッドの総数がmaximumPoolSizeに達すると、タスクをタスクキューに入れようとし続けます。
  4. バッファキューもいっぱいの場合、挿入は失敗し、拒否戦略が実行されます。

Tomcat と JDK スレッド プールの違いは、ステップ 3 にあります。スレッドの総数が最大数に達すると、Tomcat はすぐに拒否戦略を実行せず、タスク キューにタスクを追加しようとします。追加が失敗すると、拒否戦略が再度実行されます

具体的にはどのように実現されるのでしょうか?

public void execute(実行可能なコマンド、長いタイムアウト、TimeUnit 単位) {
    送信されたCountを増加して取得します。
    試す {
        //タスクを実行するには、JDK ネイティブ スレッド プールの execute 関数を呼び出します。super.execute(command);
    } キャッチ (RejectedExecutionException rx) {
       // スレッドの総数がmaximumPoolSizeに達すると、JDKネイティブスレッドプールはデフォルトの拒否ポリシーを実行します。if (super.getQueue() instanceof TaskQueue) {
            最終的な TaskQueue キュー = (TaskQueue)super.getQueue();
            試す {
                // タスクをタスクキューに追加し続ける if (!queue.force(command, timeout, unit)) {
                    送信されたCountを減分して取得します。
                    // バッファ キューがまだいっぱいの場合、挿入は失敗し、拒否戦略が実行されます。
                    新しい RejectedExecutionException("...") をスローします。
                }
            } 
        }
    }
}

タスクキューのカスタマイズ

Tomcat スレッド プールの execute メソッドの最初の行:

送信されたCountを増加して取得します。

タスクが失敗して例外がスローされると、カウンターは 1 つ減ります。

送信されたCountを減分して取得します。

Tomcat スレッド プールは、 submittedCount変数を使用して、スレッド プールに送信されたが実行されていないタスクの数を維持します。

なぜそのような変数を維持するのでしょうか?

Tomcat のタスク キュー TaskQueue は、JDK の LinkedBlockingQueue を拡張します。Tomcat はこれに容量を与え、それを親クラス LinkedBlockingQueue のコンストラクターに渡します。

パブリッククラスTaskQueueはLinkedBlockingQueue<Runnable>を拡張します。

  パブリックタスクキュー(int 容量) {
      スーパー(容量);
  }
  ...
}

容量パラメータは Tomcat の maxQueueSize パラメータによって設定されますが、maxQueueSize のデフォルト値はInteger.MAX_VALUE です。現在のスレッド数がコア スレッド数に達した後、別のタスクがある場合、スレッド プールはタスクをタスク キューに追加し、常に成功するので、新しいスレッドを作成する機会はありません。

この問題を解決するために、TaskQueue は LinkedBlockingQueue#offer を書き換え、適切なタイミングでタスクの追加が失敗したことを示す false を返します。このとき、スレッド プールは新しいスレッドを作成します。

適切な時期はいつですか?

パブリッククラスTaskQueueはLinkedBlockingQueue<Runnable>を拡張します。

  ...
   @オーバーライド
  // スレッドプールがタスクキューメソッドを呼び出すと、現在のスレッド数 > コアスレッド数 public boolean offer(Runnable o) {

      // スレッド数が最大に達した場合、新しいスレッドは作成できず、(parent.getPoolSize() == parent.getMaximumPoolSize()) の場合にのみタスク キューに追加できます。 
          super.offer(o) を返します。
          
      // これまでのところ、最大スレッド数 > 現在のスレッド数 > コアスレッド数であることを示しています // 新しいスレッドを作成できることを示しています:
      
      // 1. 送信されたタスクの数 < 現在のスレッド数の場合 // アイドル状態のスレッドがまだ存在し、新しいスレッドを作成する必要がないことを意味します if (parent.getSubmittedCount()<=(parent.getPoolSize())) 
          super.offer(o) を返します。
          
      // 2. 送信されたタスクの数 > 現在のスレッド数の場合 // スレッドが足りない場合は、false を返して新しいスレッドを作成します if (parent.getPoolSize()<parent.getMaximumPoolSize()) 
          false を返します。
          
      // デフォルトでは、タスクは常にタスクキューに入れられます。 return super.offer(o);
  }
  
}

したがって、Tomcat は、タスク キューの長さが無限の場合にスレッド プールが新しいスレッドを作成できるように、送信されたタスクの数を維持します。

Tomcat が JDK ネイティブ スレッド プールのバグを修正する方法について説明したこの記事はこれで終わりです。Tomcat JDK ネイティブ スレッド プールの詳細については、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Tomcatはスレッドプールを使用してリモート同時リクエストを処理します。
  • Tomcat ベースの接続数とスレッドプールの詳細な説明
  • JDK スレッド プールと Spring スレッド プールの使用例の分析
  • jdk独自のスレッドプールインスタンスの詳細な説明

<<:  ウェブサイトの速度を上げる6つの方法

>>:  Vue での props の使い方の紹介

推薦する

Windows 7 で Python 3.4 を使って MySQL データベースを使用する

Python 3.4でMySQLデータベースを使用する詳細なプロセスは次のとおりです。 Window...

MySQL データをエクスポートする際の secure-file-priv 問題の解決方法

エラー 1290 (HY000) : MySQL サーバーは –secure-file-priv オ...

Tomcat のパフォーマンス最適化のための Apr モジュールの構築方法

序文Tomcat は、無数のチューニング オプションを備えた、広く使用されている Java Web ...

IE6はmin-widthを実装している

まず第一に、この効果は古い話題であるはずだということはわかっています。今日ファイルを整理していたら、...

vue-routeルーティング管理のインストールと設定方法

導入Vue Router 、 Vue.jsの公式ルーティング マネージャーです。 Vue.jsのコア...

シンプルなウェブデザインコンセプトのカラーマッチング

(I)ウェブページのカラーマッチングの基本概念(1)白黒の言葉は永遠のテーマです。誰もそれを悪く言う...

JavaScript 配列重複排除ソリューション

目次方法1: set: データ型ではなくデータ構造であり、メンバーは一意である方法2: オブジェクト...

Linux でリモート サーバー ファイルの状態を表示する方法

以下のように表示されます。 test コマンドはファイルが存在するかどうかを判断します。 ssh u...

テーブル関連の配置とJavascript操作テーブル、tr、td

適切に機能するテーブル プロパティ設定:コードをコピーコードは次のとおりです。 <テーブル セ...

JS で列挙をシミュレートする方法

序文現在の JavaScript には列挙の概念がありません。一部のシナリオでは、列挙を使用するとデ...

CentOS 6.2 に MySQL 5.7.28 をインストールするチュートリアル (mysql ノート)

1. 環境整備1.MySQLインストールパス: /usr/local 2. CentOS 6.2 ...

VMware 構成 VMnet8 ネットワーク方法の手順

目次1. はじめに2. 設定手順1. はじめに1. NAT モード (VMnet8) は、仮想マシン...

CentOS7にPHP7 Redis拡張機能をインストールする方法

導入前回の記事では、Redis をインストールして設定しましたが、まだ終わりではありません。PHP ...

MySQL構成SSL証明書ログインの実装

目次序文1. MySQLはSSL構成を有効にする1.1 SSLが有効になっているかどうかを確認する1...

Vue ローカルコンポーネントデータ共有 Vue.observable() の使用

コンポーネントが詳細になるにつれて、複数のコンポーネントが状態を共有する状況に遭遇するでしょう。Vu...