SimpleAsyncTaskExecutor запускает только 8 потоков - PullRequest
1 голос
/ 19 сентября 2019

Я пытаюсь понять поведение @Async в Spring Boot, используя по умолчанию SimpleAsyncTaskExecutor (где я не определяю явно какой-либо bean-компонент Executor).Согласно документации SimpleAsyncTaskExecutor, «по умолчанию количество одновременных потоков не ограничено».Но при запуске приведенного ниже примера кода все, что я вижу, это то, что только 8 потоков запущены, а оставшиеся задачи ждут, чтобы новый поток выполнил их.Я знаю, что это можно предотвратить с помощью пользовательского Executor, где я могу определить размер пула потоков.Но я хочу знать, является ли мое понимание SimpleAsyncTaskExecutor правильным или нет, или что-то не так с моим кодом.

Основной класс

@SpringBootApplication
@EnableAsync
public class MainRunner {

    private static final Logger LOGGER = LoggerFactory.getLogger(MainRunner.class);

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(MainRunner.class);
        MyService myService = (MyService) applicationContext.getBean("myService");
        LOGGER.info("Starting the submission of tasks...");
        for (int i = 1; i <= 50; i++) {
            myService.doSomething("Number" + i);
        }
        LOGGER.info("Finished submission of tasks...");

    }
}

Класс MyService

@Service
public class MyService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyService.class);

    @Async
    public void doSomething(String userName) {
        LOGGER.info(Thread.currentThread().getName() + ", "
                + Thread.currentThread().getId() + ", NAME: " + userName + " STARTING...");
        for (int i = 0; i < 10000; i++) {
            for (int j = 0; j < 1000000; j++) {
                int res = i + j;
            }
        }
        LOGGER.info(Thread.currentThread().getName() + ", "
                + Thread.currentThread().getId() + ", NAME: " + userName + " COMPLETE...");
    }
}

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

2019-09-19 09:33:06.560  INFO 17376 --- [           main] sample.MainRunner                        : Starting the submission of tasks...
2019-09-19 09:33:06.564  INFO 17376 --- [           main] sample.MainRunner                        : Finished submission of tasks...
2019-09-19 09:33:06.566  INFO 17376 --- [         task-8] sample.MyService                         : task-8, 45, NAME: Number8 STARTING...
2019-09-19 09:33:06.566  INFO 17376 --- [         task-1] sample.MyService                         : task-1, 38, NAME: Number1 STARTING...
2019-09-19 09:33:06.566  INFO 17376 --- [         task-7] sample.MyService                         : task-7, 44, NAME: Number7 STARTING...
2019-09-19 09:33:06.567  INFO 17376 --- [         task-4] sample.MyService                         : task-4, 41, NAME: Number4 STARTING...
2019-09-19 09:33:06.566  INFO 17376 --- [         task-6] sample.MyService                         : task-6, 43, NAME: Number6 STARTING...
2019-09-19 09:33:06.567  INFO 17376 --- [         task-2] sample.MyService                         : task-2, 39, NAME: Number2 STARTING...
2019-09-19 09:33:06.567  INFO 17376 --- [         task-5] sample.MyService                         : task-5, 42, NAME: Number5 STARTING...
2019-09-19 09:33:06.567  INFO 17376 --- [         task-3] sample.MyService                         : task-3, 40, NAME: Number3 STARTING...

Он ожидает завершения первых 8, а затем выполняются оставшиеся задачи.Мое понимание SimpleAsyncTaskExecutor здесь неверно?

Ответы [ 2 ]

1 голос
/ 19 сентября 2019

Ваш код не использует SimpleAsyncTaskExecutor.

Использование @ EnableAsync просто

Включает возможность выполнения асинхронного метода Spring, аналогично функциональности, найденной в пространстве имен Spring XML.

Spring не создает SimpleAsyncTaskExecutor на основе этой аннотации.Просмотр выходных данных журнала:

2019-09-19 12: 45: 43.475 INFO 19660 --- [main] ossconcurrent.ThreadPoolTaskExecutor: Initializing ExecutorService 'applicationTaskExecutor'

Похоже, что Spring создает его, он выглядит как ThreadPoolTaskExecutor по умолчанию, который, вероятно, привязывает количество ядер на вашем компьютере (хотя я на самом деле не проверял).

Если вы действительно хотите SimpleAsyncTaskExecutor, вы можете реализоватьИнтерфейс AsyncConfigurer в вашей конфигурации

@SpringBootApplication
@EnableAsync
public class MainRunner implements AsyncConfigurer {

  private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(MainRunner.class);

  @Override
  public Executor getAsyncExecutor() {
      return new SimpleAsyncTaskExecutor();
  }

  public static void main(String[] args) {
    ApplicationContext applicationContext = SpringApplication.run(MainRunner.class);
    MyService myService = (MyService) applicationContext.getBean("myService");
    LOGGER.info("Starting the submission of tasks...");
    for (int i = 1; i <= 50; i++)
    {
      myService.doSomething("Number" + i);
    }
    LOGGER.info("Finished submission of tasks...");
  }
}
1 голос
/ 19 сентября 2019

Да, потоки ограничены доступными ядрами в ЦП здесь

Ядро

Ядро обычно является базовой вычислительной единицей ЦП -он может запускать один программный контекст (или несколько, если он поддерживает аппаратные потоки, такие как гиперпоточность на процессорах Intel)

CPU

CPU может иметь одно или несколько ядервыполнять задачи в данный момент.Этими задачами обычно являются программные процессы и потоки, запланированные ОС.Обратите внимание, что ОС может иметь много потоков для запуска, , но ЦП может выполнять только X таких задач в данный момент времени, где X = число ядер * количество аппаратных потоков на ядро. Остальным придется подождатьдля операционной системы, чтобы планировать их, вытесняя текущие задачи или любым другим способом.

Что произойдет, если у вас будет больше потоков?

Предположим, если у вас естьЧисло потоков X, тогда планировщик ЦП дает каждому из этих потоков X некоторую долю времени ЦП.Некоторые потоки будут работать параллельно (если у вас есть 4 ядра, то одновременно будут работать 4 потока одновременно или если у вас есть 4-ядерная гиперпоточность на процессорах Intel, то в общей сложности 8 потоков будут работать параллельно), а остальные потоки будутв ожидании или одновременно. Вы можете использовать эту команду, чтобы найти число доступных процессоров Runtime.getRuntime().availableProcessors()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...