作業シナリオでは、ホストの IP アドレスに基づいて他のモデルの関連情報を更新するという要件に遭遇しました。要件は非常にシンプルで、一般的なデータベース連携のクエリと更新操作のみを必要としますが、コーディング実装の過程で、ホストの数が多いため、クエリと更新のループに長い時間がかかることが判明しました。インターフェイスを 1 回呼び出すのに約 30 ~ 40 秒かかります。操作が完了するまでの分時間。
したがって、インターフェイス メソッドの実行時間を効果的に短縮するには、マルチスレッド同時プログラミング手法の使用、マルチコア プロセッサの並列実行機能の活用、データの非同期処理を検討してください。実行時間を短縮し、効率を向上させます。
ここでは、固定数のスレッドを持つ再利用可能なスレッド プール FixedThreadPool
が使用され、CountDownLatch
同時実行ツール クラスによって提供される同時プロセス制御ツールが併用されます。マルチスレッドの同時実行性を確保するため プログラミング中の通常の操作:
最初に、Runtime.getRuntime() を通じて実行中のマシンの CPU スレッドを取得します。 availableProcessors() メソッド Number。その後、固定スレッド プール内のスレッド数を設定するために使用されます。
2 番目に、 タスクの特性を決定します。計算負荷の高いタスクの場合は、スレッド数を CPU スレッド番号 1# に設定します。 ##、IO の場合、集中的なタスクの場合、スレッド数を 2 * CPU スレッドの数
に設定します。この方法ではデータベースと頻繁に対話する必要があるため、IO 集中型のタスクになります。
その後、データがグループ化されて切り取られます。各スレッドは 1 つのグループ化されたデータを処理します。グループ化されたグループの数はスレッドの数と一致し、オブジェクト CountDownLatch はコンストラクターを呼び出し、初期化パラメーター値はスレッドの数であり、メイン スレッドはすべての子スレッドの実行が完了するまで待機してから後続の操作を実行します。
、executorService.execute() メソッドを呼び出し、run
メソッドを書き換えてビジネス ロジックを記述します。データ処理コードでは、現在のスレッドの実行後にカウンターを 1 ずつデクリメントすることを忘れないでください。 最後に
、すべてのサブスレッドが完了したら、スレッド プールを閉じます。
public ResponseData updateHostDept() { // ... List<Map> hostMapList = mongoTemplate.find(query, Map.class, "host"); // split the hostMapList for the following multi-threads task // return the number of logical CPUs int processorsNum = Runtime.getRuntime().availableProcessors(); // set the threadNum as 2*(the number of logical CPUs) for handling IO Tasks, // if Computing Tasks set the threadNum as (the number of logical CPUs) + 1 int threadNum = processorsNum * 2; // the number of each group data int eachGroupNum = hostMapList.size() / threadNum; List<List<Map>> groupList = new ArrayList<>(); for (int i = 0; i < threadNum; i++) { int start = i * eachGroupNum; if (i == threadNum - 1) { int end = mapList.size(); groupList.add(hostMapList.subList(start, end)); } else { int end = (i+1) * eachGroupNum; groupList.add(hostMapList.subList(start, end)); } } // update data by using multi-threads asynchronously ExecutorService executorService = Executors.newFixedThreadPool(threadNum/2); CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (List<Map> group : groupList) { executorService.execute(()->{ try { for (Map map : group) { // update the data in mongodb } } catch (Exception e) { e.printStackTrace(); } finally { // let counter minus one countDownLatch.countDown(); } }); } try { // main thread donnot execute until all child threads finish countDownLatch.await(); } catch (Exception e) { e.printStackTrace(); } // remember to shutdown the threadPool executorService.shutdown(); return ResponseData.success(); }
次に、マルチスレッドの非同期更新戦略を使用した後、インターフェイスの呼び出しに必要なおおよその時間が
30-40 min から 8-10 min
に短縮され、実行効率が大幅に向上しました。
newFixedThreadPoolには、ブロッキング キューのデフォルトが無制限のキューになり、デフォルト値が
であるという欠陥があることに注意してください。 Integer.MAX_VALUE
は OOM 問題を引き起こす可能性が非常に高くなります。したがって、通常はThreadPoolExecutor
を使用してスレッド プールを作成し、待機キュー内のスレッドの数を指定して OOM 問題を回避できます。上記のコードでは、コア スレッドの数とスレッドの最大数はそれぞれ 5 と 8 ですが、大きな値に設定すると、あまり大きな値には設定されません。コンテキストの切り替えにより時間の消費も増加しますが、マルチスレッドの利点を最大限に活用することはできません。適切なパラメータをどのように選択するかについては、マシンのパラメータとタスクの種類に基づいて決定する必要があります。public ResponseData updateHostDept() { // ... List<Map> hostMapList = mongoTemplate.find(query, Map.class, "host"); // split the hostMapList for the following multi-threads task // return the number of logical CPUs int processorsNum = Runtime.getRuntime().availableProcessors(); // set the threadNum as 2*(the number of logical CPUs) for handling IO Tasks, // if Computing Tasks set the threadNum as (the number of logical CPUs) + 1 int threadNum = processorsNum * 2; // the number of each group data int eachGroupNum = hostMapList.size() / threadNum; List<List<Map>> groupList = new ArrayList<>(); for (int i = 0; i < threadNum; i++) { int start = i * eachGroupNum; if (i == threadNum - 1) { int end = mapList.size(); groupList.add(hostMapList.subList(start, end)); } else { int end = (i+1) * eachGroupNum; groupList.add(hostMapList.subList(start, end)); } } // update data by using multi-threads asynchronously ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 8, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100)); CountDownLatch countDownLatch = new CountDownLatch(threadNum); for (List<Map> group : groupList) { executor.execute(()->{ try { for (Map map : group) { // update the data in mongodb } } catch (Exception e) { e.printStackTrace(); } finally { // let counter minus one countDownLatch.countDown(); } }); } try { // main thread donnot execute until all child threads finish countDownLatch.await(); } catch (Exception e) { e.printStackTrace(); } // remember to shutdown the threadPool executor.shutdown(); return ResponseData.success(); }ログイン後にコピー最後に、コーディング以外の方法でマシンの CPU スレッド数を取得する場合も非常に簡単です。Windows システムでは、タスク マネージャーを通じて CPU スレッド数を確認できます。
上の図からわかるように、私のマシンのコアは 8 個の CPU ですが、物理コアは 1 個です。 CPU コアは、ハイパースレッディング テクノロジを通じて 2 つの論理 CPU スレッドにシミュレートできるため、私のマシンは 8 コアと 16 スレッドをサポートします。
以上がJava マルチスレッド同時プログラミングはデータ処理効率をどの程度向上させますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。