Как использовать несколько работающих интерфейсов в одном потоке?

Я разрабатываю приложение Java, которое вычисляет различные математические функции. Вот сценарий: у меня есть M исполняемых задач (каждая для вычисления различных задач, например, одна решает квадратные уравнения, другая решает экспоненциальные функции, что-то в этом роде). Эти M runnables должны выполняться каждые N минут. Эти runnables могут выполняться последовательно, не обязательно параллельно. Мне не разрешено создавать более одной темы.

Я могу использовать ScheduledExecutorService для периодического запуска задач. Согласно Javadoc, с ScheduledExecutorService можно использовать только один исполняемый объект. Существуют такие методы, как invokeAll(...), которые позволяют нам предоставить Collection runnable, но они не предоставляют опции планирования.

Просматривая Интернет, я обнаружил, что использование Thread.sleep() не является хорошим способом для разработки приложения.

Какие-либо предложения??


person Adhi    schedule 04.03.2014    source источник
comment
ScheduledExecutorService мне очень подходит для данного варианта использования. Где в документах говорится только об одном Runnable?   -  person nullpotent    schedule 04.03.2014
comment
Если вы можете запускать только одну вещь за раз и вам нужно планировать вещи, я думаю, вы могли бы использовать комбинацию кварца и jms.   -  person Leo    schedule 04.03.2014


Ответы (2)


Вы можете создать ExecutorService, содержащий только один поток для выполнения ваших заданий:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Когда вы отправляете несколько заданий в эту службу с помощью метода invokeAll, они будут выполняться последовательно, используя один экземпляр Thread.

Если вы хотите использовать ScheduledExecutorService для запуска своих заданий каждые N минут, вы можете переключиться на

ScheduledExecutorService scheduledExecutorService = 
    Executors.newSingleThreadScheduledExecutor();

которые предоставят вам дополнительные методы для лучшего контроля вашей работы.

Как видите, метод invokeAll является производным от ExecutorService, поэтому он не предоставляет вам возможности планирования. Тем не менее, это всего лишь ярлык, и вы можете запланировать несколько экземпляров Runnable, используя обычный цикл:

for (Runnable job : allJobs) {
    scheduledExecutorService.scheduleAtFixedRate(job, 0L, N, TimeUnit.MINUTES);
}
person hoaz    schedule 04.03.2014
comment
Когда вы хотите, чтобы все они запускались одновременно и последовательно, кажется немного странным планировать их все по отдельности. Почему бы просто не объединить их в один исполняемый файл, как мой ответ? - person Cruncher; 04.03.2014
comment
Автор должен иметь возможность выбрать то, что ему больше нравится, поэтому у нас есть возможность дать несколько ответов. К вашему сведению - задания все равно не будут выполняться одновременно, поэтому не имеет значения, запускаете ли вы их в рамках одного запланированного задания или используете несколько заданий. - person hoaz; 04.03.2014
comment
@Cruncher Я думаю, что на самом деле это более чистое и гибкое решение, если оно работает правильно. - person Giovanni Botta; 04.03.2014
comment
Это идея, которую я имел в виду. Я протестировал его, и он работает правильно. Он запускает каждый работающий интерфейс последовательно через N минут. Я пытался выяснить, как он планирует каждый исполняемый файл, но не смог. Можете ли вы объяснить внутренний поток планировщика?? И спасибо за ответ :) - person Adhi; 05.03.2014
comment
Внутри есть очередь заданий с назначенными временными метками. Каждый поток в пуле потоков исполнителя выбирает задание из очереди, если оно доступно (отметка времени ›= сейчас), выполняет его, выбирает следующее и так далее. Служба-исполнитель ставит одно и то же задание в очередь, если оно является периодическим. - person hoaz; 05.03.2014

Если вы создадите несколько исполняемых классов, как показано ниже, вы можете передать его экземпляр своему планировщику, и он запустит все исполняемые модули, с которыми вы его создали. Планировщик делает все остальное, он просто последовательно запускает массив runnables.

public class MultipleRunnable implements Runnable
{
    private Runnable[] commands;
    public MultipleRunnable(Runnable[] commands)
    {
        this.commands = commands;
    }

    public void run()
    {
        for(int i = 0 ; i < commands.length ; i++)
        {
             commands[i].run();
        }
    }
}

Что-то вроде этого, чтобы начать это должно работать:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(
new MultipleRunnable(new Runnable[]{run1, run2, ..., runM}),
n, n, TimeUnit.MINUTES);
person Cruncher    schedule 04.03.2014
comment
Я могу создавать M-классы вместо исполняемых и вызывать их последовательно внутри одного исполняемого интерфейса. Затем мне нужно запланировать только один исполняемый объект с помощью ScheduledExecutorService. Он также выполняет работу для данного сценария. Кроме того, работающие интерфейсы здесь не используются должным образом. Но мне нужно использовать несколько работающих интерфейсов для гибкости приложения. В любом случае, спасибо за ответ :) - person Adhi; 05.03.2014
comment
@Adithyan Как это неправильно использует интерфейсы Runnable? Кроме того, этот класс довольно абстрактен, он не говорит вам, как определить все runnables. Прочитав вопрос, я предположил, что у вас уже есть список исполняемых файлов. Это просто берет вашу группу исполняемых модулей и объединяет их в один исполняемый объект, который вы можете передать всему, что принимает исполняемый объект. - person Cruncher; 05.03.2014
comment
@Adithyan Adithyan Вы правы, это сработает для данного сценария. Но это решение должно было бы жестко кодировать его каждый раз. Это ОБЩЕЕ решение для ЛЮБОЙ группы Runnable Objects. - person Cruncher; 05.03.2014