Как да използвам множество изпълняваеми интерфейси в една нишка?

Разработвам java приложение, което изчислява различни математически функции. Ето сценария, имам M изпълняваеми задачи (всяка за изчисляване на различни проблеми, като една решава квадратни уравнения, друга решава експоненциални функции, нещо подобно). Тези M runnables трябва да се изпълняват на всеки N минути. Тези runnables могат да се изпълняват последователно, не непременно паралелно. Не ми е позволено да създавам повече от една нишка.

Мога да използвам ScheduledExecutorService за периодично изпълнение на задачите. Според Javadoc само един runnable може да се използва със ScheduledExecutorService. Има методи като invokeAll(...), които ни позволяват да предоставим Collection of 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

Ако създадете множество изпълнени класове като следния, можете да предадете негов екземпляр на вашия планировчик и той ще стартира всички изпълнявани класове, с които сте го инстанциирали. Планировчикът върши останалото, това само последователно изпълнява масива от изпълнявани елементи.

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 класове вместо runnable и да ги извиквам последователно, в един единствен runnable интерфейс. След това трябва да планирам само един runnable с помощта на ScheduledExecutorService. Той също върши работата за даден сценарий. Освен това, работещите интерфейси не се използват правилно тук. Но трябва да използвам множество работещи интерфейси за гъвкавост на приложението. Както и да е, благодаря за отговора :) - person Adhi; 05.03.2014
comment
@Adithyan Как това използва неправилно Runnable интерфейси? Също така, този клас е доста абстрактен, той не ви казва как да дефинирате всички runnables. Като прочетох въпроса, предположих, че вече имате своя списък с runnables. Това просто взема вашата група runnable и ги обвива в един runnable, който можете да прехвърлите към всичко, което приема runnable. - person Cruncher; 05.03.2014
comment
@Adithyan Прав си, това ще работи за дадения сценарий. Но това решение ще трябва да го кодира твърдо всеки път. Това е ОБЩО решение за ВСЯКА група Runnable Objects. - person Cruncher; 05.03.2014