Почему вы ограничиваете себя таким небольшим количеством потоков?
Таким образом, вы упускаете возможности для повышения производительности. Кажется, что ваши задачи действительно не ограничены процессором. Сетевые операции (удаленный сервис + запрос к базе данных) могут занимать большую часть времени для завершения каждой задачи. В это время, когда одна задача / поток должен ждать какого-то события (сеть, ...), другой поток может использовать ЦП. Чем больше потоков вы делаете доступными для системы, тем больше потоков может ожидать завершения сетевого ввода-вывода, в то время как некоторые потоки одновременно используют ЦП.
Я предлагаю вам резко увеличить количество потоков для исполнителя. Поскольку вы говорите, что оба удаленных сервера используются недостаточно, я предполагаю, что хост, на котором работает ваша программа, является узким местом на данный момент. Попробуйте увеличить (удвоить?) Количество потоков, пока узкое место не станет равным загруженности вашего процессора до 100%, или память или удаленная сторона.
Кстати, вы shutdown
исполнитель, но вы на самом деле ждете завершения задач? Как вы измеряете «QPS»?
Мне приходит в голову еще одна вещь: как обрабатываются соединения с БД? То есть как синхронизируются SaveToDatabase()
с? Все потоки разделяют (и конкурируют) одно соединение? Или, что еще хуже, каждый поток создаст новое соединение с БД, сделает свое дело, а затем снова закроет соединение? Это может быть серьезным узким местом, поскольку установление TCP-соединения и выполнение аутентификации может потребовать столько же времени, сколько и выполнение простого оператора SQL.
Если число идентификаторов в идентификаторах превышает 50000, целесообразно ли использовать
invokeAll? Должны ли мы разделить его на более мелкие партии, такие как 5000 каждая
партия?
Как уже писал @Vaclav Stengl, у Исполнителей есть внутренние очереди, в которые они ставят в очередь и из которых они обрабатывают задачи. Так что не нужно беспокоиться об этом. Вы также можете просто вызвать submit
для каждой отдельной задачи, как только вы ее создали. Это позволяет уже начать выполнение первых задач, пока вы еще создаете / готовите более поздние задачи, что имеет смысл, особенно когда каждая задача создание занимает сравнительно много времени, но не повредит во всех других случаях. Думайте о invokeAll
как об удобном методе для случаев, когда у вас уже есть набор задач. Если вы создаете задачи последовательно самостоятельно и у вас уже есть доступ к ExecutorService
для их выполнения, просто submit()
их a.s.a.p.