CPU が 100% 稼働しているという事実は、CPU が有用な作業をどれだけビジー状態で行っているかを示すものではありません。あなたの場合、コアよりも多くのスレッドを使用しているため、100% にはコンテキストの切り替えが含まれており、不必要にメモリを使用しています (100 スレッドの場合はわずかな影響)。これは最適ではありません。
CPU を集中的に使用するタスクでは、通常、次のイディオムを使用します。
private final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1;
private final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
他の人が示したように、より多くのスレッドを使用すると、不要なコンテキスト切り替えが導入されるだけです。
明らかに、タスクが何らかの I/O やその他のブロック操作を行う場合、これは当てはまらず、より大きなプールが理にかなっています。
編集
@MartinJames のコメントに返信するために、(単純な) ベンチマークを実行しました。結果は、プール サイズ = プロセッサ数 + 1 から 100 にすると、パフォーマンスがわずかに低下する (5% としましょう) ことを示しています。 1000 および 10000) は、パフォーマンスに大きな影響を与えます。
結果は 10 回の実行の平均です。
プール サイズ: 9: 238 ミリ秒。//(NUM_CORES+1)
プール サイズ: 100: 245 ミリ秒。
プール サイズ: 1000: 319 ミリ秒。
プール サイズ: 10000: 2482 ミリ秒。
コード:
public class Test {
private final static int NUM_CORES = Runtime.getRuntime().availableProcessors();
private static long count;
private static Runnable r = new Runnable() {
@Override
public void run() {
int count = 0;
for (int i = 0; i < 100_000; i++) {
count += i;
}
Test.count += count;
}
};
public static void main(String[] args) throws Exception {
//warmup
runWith(10);
//test
runWith(NUM_CORES + 1);
runWith(100);
runWith(1000);
runWith(10000);
}
private static void runWith(int poolSize) throws InterruptedException {
long average = 0;
for (int run = 0; run < 10; run++) { //run 10 times and take the average
Test.count = 0;
ExecutorService executor = Executors.newFixedThreadPool(poolSize);
long start = System.nanoTime();
for (int i = 0; i < 50000; i++) {
executor.submit(r);
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
long end = System.nanoTime();
average += ((end - start) / 1000000);
System.gc();
}
System.out.println("Pool size: " + poolSize + ": " + average / 10 + " ms. ");
}
}