Оптимальный способ создания пула потоков фиксированного размера в Java с использованием сервиса Executors

Я использую Executors фреймворк в Java для создания пулов потоков для многопоточного приложения, и у меня есть вопрос, связанный с производительностью.

У меня есть приложение, которое может работать в режиме реального времени или не в режиме реального времени. Если это в режиме реального времени, я просто использую следующее:

THREAD_POOL = Executors.newCachedThreadPool();

Но если это не в реальном времени, я хочу иметь возможность контролировать размер моего пула потоков. Чтобы сделать это, я думаю о двух вариантах, но я не совсем понимаю разницу, и какой из них будет работать лучше.

Вариант 1 - использовать простой способ:

THREAD_POOL = Executors.newFixedThreadPool(threadPoolSize);

Вариант 2 - создать свой собственный ThreadPoolExecutor как это:

RejectedExecutionHandler rejectHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    try {
        executor.getQueue().put(r);
    } catch (Exception e) {}
}
};          
THREAD_POOL = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000), rejectHandler);

Я хотел бы понять, в чем преимущество использования более сложного варианта 2, а также, если я должен использовать другую структуру данных, чем LinkedBlockingQueue? Любая помощь будет оценена.

1 ответ

Решение

Глядя на исходный код, вы поймете, что:

Executors.newFixedThreadPool(threadPoolSize);

эквивалентно:

return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, MILLISECONDS,
                              new LinkedBlockingQueue<Runnable>());

Так как это не обеспечивает явного RejectedExecutionHandler, дефолт AbortPolicy используется. Это в основном кидает RejectedExecutionException как только очередь заполнится. Но очередь не ограничена, поэтому она никогда не будет полной. Таким образом, этот исполнитель принимает1 ряд заданий.

Ваша декларация намного сложнее и совсем другая:

  • new LinkedBlockingQueue<Runnable>(10000) приведет к тому, что пул потоков откажется от задач, если ожидается более 10000.

  • Я не понимаю какой ты RejectedExecutionHandler делается. Если пул обнаруживает, он не может поместить больше runnables в очередь, которую он вызывает ваш обработчик. В этом обработчике вы... попробуйте поместить это Runnable снова в очередь (которая потерпеть неудачу, как в 99% случаев блок). Наконец вы проглотили исключение. Похоже на ThreadPoolExecutor.DiscardPolicy это то, что вы после.

    Если вы посмотрите на ваши комментарии ниже, то, похоже, вы пытаетесь заблокировать или каким-то образом ограничить клиентов, если очередь задач слишком велика. Я не думаю, что блокировка внутри RejectedExecutionHandler хорошая идея Вместо этого рассмотрим CallerRunsPolicy политика отказа. Не совсем то же самое, но достаточно близко.

Подводя итог: если вы хотите ограничить количество ожидающих задач, ваш подход почти хорош. Если вы хотите ограничить количество одновременных потоков, достаточно первого однострочного.

1 - предполагая, что 2^31 - бесконечность

Другие вопросы по тегам