Java ExecutorService последовательно - Spring MVC

Я хочу реализовать ExecutorService в моем приложении Spring-MVC.

Мне нужен глобальный ExecutorService, который принимает задачи, помещает их в очередь, а затем выполняет их последовательно. Поэтому я хочу передавать задачи из разных мест в приложении. Поэтому я использую Executors.newSingleThreadExecutor();, поэтому у меня есть только 1 поток, выполняющий эти задачи.

Однако я не уверен, как интегрировать его в приложение Spring:

public enum TaskQueue {

    INSTANCE;

    ExecutorService executorService;

    private TaskQueue() {
        executorService = Executors.newSingleThreadExecutor();
    }

    public void addTaskToQueue (Runnable task){
        executorService.submit(task);
        executorService.shutdown();
    }
}

Итак, я думаю о создании синглтона, а затем просто передать задачу (объект Runnable) методу:

TaskQueue.INSTANCE.addTaskToQueue(new Runnable() {
 @Override
  public void run() {
      System.out.println("Executing Task1 inside : " + Thread.currentThread().getName());
        }
    });

Однако у меня есть несколько вопросов:

Я не уверен, как интегрировать его в приложение Spring MVC, где у меня есть контроллеры, службы и так далее.

Приложение получает некоторые уведомления от веб-службы. Эти уведомления будут обрабатываться в разных местах кода. Я хочу выполнять их последовательно. Поэтому мне нужно определить все задачи, которые я хочу запускать асинхронно, а затем передать их указанному выше методу (`addTaskToQueue), заключенному в объект Runnabel, чтобы их можно было выполнять асинхронно. Это правильный подход?

Поэтому я всегда передаю объект Runnable этому методу для его выполнения. Этот метод выполняет его и завершает работу службы исполнителя. Поэтому каждый раз, когда я передаю задачу этой службе, она создает новую службу, а затем закрывает ее. Но я не хочу этого - я хочу, чтобы служба исполнителя оставалась в живых и выполняла задачи, которые поступают, а не завершалась после каждой задачи. Как мне этого добиться?

Или я совершенно не прав, реализуя это таким образом?

Обновлено:

Используя TaskExecutor, предоставляемый Spring, я бы реализовал его так:

@Autowired
private TaskExecutor taskExecutor;

Затем вызывая его из другого места в моем коде:

taskExecutor.execute(new Runnable() {
        @Override
        public void run() {
            //TODO add long running task
        }
    });

Вероятно, мне нужно где-то в конфигурации убедиться, что он должен выполняться последовательно, поэтому он должен создавать только 1 поток. Насколько безопасно звонить из разных мест? Он не всегда будет создавать новую услугу с новой очередью?

Почему вы выключаете ExecutorService сразу после добавления к нему задачи?

SeverityOne 01.05.2018 20:09

@SeverityOne Но когда мне нужно его выключить? Я хочу передавать задачи из нескольких мест в приложении Spring, и я не знаю, где поставить завершение работы.

Norbert94 01.05.2018 20:10

Когда он вам больше не нужен. Я лучше напишу на это ответ.

SeverityOne 01.05.2018 20:11

Моя самая большая проблема - это интеграция всей концепции в приложение Spring MVC. У меня есть метод веб-службы, который получает все уведомления, а затем вызываются другие службы для выполнения некоторых задач. Может быть, мне нужно поставить его в конце метода веб-службы?

Norbert94 01.05.2018 20:14

@ Norbert94 вы устанавливаете maxNumber потоков при настройке bean-компонента taskExecutor в XML или в конфигурации Java. По умолчанию bean-компоненты Spring являются одиночными, поэтому для каждого контекста приложения будет создан только один экземпляр taskExecutor.

Ivan 01.05.2018 20:35

@Ivan Да, извините, это был глупый вопрос. Конечно, у Spring beans есть только один экземпляр. Согласны ли вы с вызовом этого метода в разных службах в моем приложении Spring? Возможно, мне не хватило бы памяти, если бы в очереди было много задач

Norbert94 01.05.2018 20:40

@ Norbert94, если вы боитесь получить OOM в случае добавления большого количества задач в очередь, вы можете создать taskExecutor с более чем одним потоком. Также, если у вас разные задачи (один набор задач является длительным, а другой набор задач быстро выполняется), вам лучше иметь два разных bean-компонента taskExecutor для этих двух типов задач. Но в любом случае можно использовать этот бин из нескольких других бобов.

Ivan 01.05.2018 20:44
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
7
1 317
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В Spring уже есть компонент для этого: org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor. Он реализует интерфейс DisposableBean, и метод shutdown() вызывается при уничтожении контекста Spring.

  <bean id = "taskExecutor" class = "org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name = "corePoolSize" value = "${threadPoolSize}"/>
    <property name = "maxPoolSize" value = "${threadPoolSize}"/>
    <property name = "WaitForTasksToCompleteOnShutdown" value = "false"/>
  </bean>

Спасибо за ответ - я отредактировал свой вопрос и предоставил образец кода для этого подхода - я был бы признателен, если бы вы могли его проверить.

Norbert94 01.05.2018 20:33

Шаблон проектирования Singleton не очень подходит для этого. Поскольку вы используете Spring, имеет смысл создавать компонент с методами @PostConstruct и @PreDestroy. Здесь и здесь - статьи, объясняющие это.

В вашем случае я бы сделал что-то вроде следующего:

@Component
public class TaskQueue {

    private ExecutorService executorService;

    @PostConstruct
    public void init() {
        executorService = Executors.newSingleThreadExecutor();
    }

    @PreDestroy
    public void cleanup() {
        executorService.shutdown();
    }

    public void addTaskToQueue (Runnable task){
        executorService.submit(task);
    }
}

И затем вы можете автоматически подключить этот компонент.

Обновлено: @ Иван ответ предпочтительнее.

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