ときcorePoolSize = 0 ScheduledExecutorServiceは、100%のCPUを消費します

ミハイルKholodkov:

私は生産の興味深い問題に遭遇しました。

私は以下の持っていたScheduledThreadPool割り当てコード:

ScheduledExecutorService executorService =
            Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() - 1);

スレッドプールは、定期的にキューからいくつかのタスクを処理していました。そして、すべてのものは、サービスが上に展開された瞬間までの罰金を働いていたシングルコア環境。どうやら、に変換上記の行:

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

それ以来、JVMプロセスのCPU使用率が常に100%前後でした。私が変更した瞬間Runtime.getRuntime().availableProcessors() - 1定数に1問題が行っています。

これは、根本的な原因を見つけるためにいつか取ったが、それでも私はその背後にある理由を知りません。ScheduledExecutorServiceJavaDocの状態:

/**
 * Creates a thread pool that can schedule commands to run after a
 * given delay, or to execute periodically.
 * @param corePoolSize the number of threads to keep in the pool,
 * even if they are idle
 * @return a newly created scheduled thread pool
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

基本的には、0(ゼロ)は、スレッドプールのインスタンス化のための有効な引数であるが、それはこの値でスーパー奇妙な動作します。

誰かがなぜこれほど説明していただけますか?

シンプルかつ検証テストケース

import java.util.Queue;
import java.util.concurrent.*;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        MessageTaskExecutor asyncEmailGatewayTaskExecutor = new MessageTaskExecutor();

        // Infinitely add new tasks to the queue every second
        for (int i = 1; ; i++) {
            System.out.println(String.format("Adding message #%s to the queue", i));

            asyncEmailGatewayTaskExecutor.putMessageIntoQueue(i);

            Thread.sleep(1_000);
        }
    }

    static class MessageTaskExecutor {

        static final int INITIAL_DELAY_SECONDS = 1;
        static final int PROCESSING_RATE_MILLISECONDS = 5_000;

        final Queue<Runnable> messageQueue = new ArrayBlockingQueue<>(1_000_000);
        final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

        MessageTaskExecutor() {
            // Scavenging Message Tasks Queue every 'PROCESSING_RATE_MILLISECONDS'. Initial delay is fixed for 'INITIAL_DELAY_SECONDS'
            executorService.schedule(this::processEmailTasks, INITIAL_DELAY_SECONDS, TimeUnit.SECONDS);
        }

        void putMessageIntoQueue(int messageId) {
            Runnable messageTask = () -> System.out.println(String.format("Message #%s is getting processed!", messageId));

            messageQueue.offer(messageTask);
        }

        void processEmailTasks() {
            System.out.println(String.format("There are %s messages in the queue. Processing the messages...", messageQueue.size()));

            // Processing messages queue
            while (!messageQueue.isEmpty()) {
                executorService.submit(messageQueue.poll()); // Submitting task to executor service
            }

            // Re-scheduling processing job
            executorService.schedule(this::processEmailTasks, PROCESSING_RATE_MILLISECONDS, TimeUnit.MILLISECONDS);
        }
    }
}

これは、コード割り当て〜30メガバイトとJVMプロセス消費〜100%CPU(上でテストシングルコア仮想マシンの勝利7 / CentOSの7)。JDK 1.8.0.181

フィールドを変更することにより:

final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(0);

に:

final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

CPUの消費量は、通常3から5パーセントにダウンしました。

apangin:

これは既知のバグです:JDK-8129861これは、JDK 9に固定されています。

この問題を回避するには1、少なくともコアプールサイズを設定することです。

int corePoolSize = Math.max(Runtime.getRuntime().availableProcessors() - 1, 1);

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=201091&siteId=1