W kolejnym z cyklu (Współbieżność w Springu cz. 1) wpisie dotyczącym współbieżności w Springu przedstawiona zostanie koncepcja planowania zadań.
Podstawowym interfejsem Spring'a realizującym tę funkcjonalność jest TaskScheduler. Poniżej przestawiona jest hierarchia wymienionego interfejsu.
TaskScheduler deklaruje poniższe metody:
Spring zapewnia 3 implementacje powyższego interfejsu:
Podstawowym interfejsem Spring'a realizującym tę funkcjonalność jest TaskScheduler. Poniżej przestawiona jest hierarchia wymienionego interfejsu.
Hierarchia interfejsu TaskScheduler |
- schedule(Runnable, Date) - podane zadanie (Runnable) uruchamiane jest w określonej dacie. Jeżeli podana data jest przeszła to zadanie zostanie niezwłocznie zlecone do uruchomienia.
- scheduleAtFixedRate(Runnable, long) - zlecane zadanie będzie uruchamiane cyklicznie z cyklem pomiędzy kolejnymi uruchomieniami określonym w milisekundach, a pierwsze uruchomienie odbędzie się najszybciej jak to możliwe.
- scheduleAtFixedRate(Runnable, Date, long) - zlecane zadanie będzie uruchamiane cyklicznie począwszy od określonej daty, cykl pomiędzy kolejnymi uruchomieniami określony jest w milisekundach.
- scheduleAtFixedDelay(Runnable, long) - zlecane zadanie będzie uruchamiane cyklicznie, z określonym cyklem w milisekundach pomiędzy końcem przetwarzania poprzedniego zadania i początkiem następnego. Pierwsze wykonanie odbędzie się najszybciej jak to możliwe.
- scheduleAtFixedDelay(Runnable, Date, long) - zlecane zadanie będzie uruchamiane cyklicznie, z określonym cyklem w milisekundach pomiędzy końcem jednego wykonania i początkiem następnego. Pierwsze wykonanie zostanie wywołane o podanej dacie.
- schedule(Runnable, Trigger) - zlecane zadanie będzie uruchamiane zgodnie z definicją - Trigger.
Spring zapewnia 3 implementacje powyższego interfejsu:
- ConcurrentTaskScheduler - jest adapterem dla ScheduledExecutorService z Javy.
- ThreadPoolTaskScheduler - opakowuje natywny ScheduledThreadPool, wystawiając jako własne właściwości parametry Javowej implementacji.
- TimerManagerTaskScheduler - implementacja opakowująca TimerManager'a z commonj.
Wróćmy jeszcze do ostatniej metody interfejsu TaskScheduler. Jest ona szczególnie interesująca, ponieważ pozwala jej klientom elastycznie określać czas uruchamiania zadań - osiągnięto to dzięki interfejsowi Trigger.
- nextExecutionTime(TriggerContext) - jedyna metoda interfejsu. Na podstawie kontekstu TriggerContext zwraca czas następnego uruchomienia zadania.
- lastActualExecutionTime - rzeczywisty czas ostatniego wykonania zadania.
- lastCompleteTime - czas zakończenia ostatniego wykonania zadania.
- lastScheduledExecutionTime - planowany czas uruchomienia ostatniego wykonania zadania.
Poniżej przedstawiono przykład wykorzystania implementacji ThreadPoolTaskScheduler.
1: package pl.com.mbsoftware.eiws.concurrency;
2:
3: import org.springframework.context.annotation.Bean;
4: import org.springframework.context.annotation.Configuration;
5: import org.springframework.scheduling.TaskScheduler;
6: import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
7:
8: @Configuration
9: public class TaskSchedulerTestSpringConfiguration {
10:
11: @Bean
12: public TaskScheduler taskScheduler() {
13: return new ThreadPoolTaskScheduler();
14: }
15:
16: }
1: package pl.com.mbsoftware.eiws.concurrency;
2:
3: import org.junit.Test;
4: import org.junit.runner.RunWith;
5: import org.slf4j.Logger;
6: import org.slf4j.LoggerFactory;
7: import org.springframework.beans.factory.annotation.Autowired;
8: import org.springframework.scheduling.TaskScheduler;
9: import org.springframework.scheduling.support.CronTrigger;
10: import org.springframework.test.context.ContextConfiguration;
11: import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12:
13: @RunWith(SpringJUnit4ClassRunner.class)
14: @ContextConfiguration(classes = { TaskSchedulerTestSpringConfiguration.class })
15: public class TaskSchedulerTest {
16:
17: private final static Logger log = LoggerFactory.getLogger(TaskSchedulerTest.class);
18:
19: @Autowired
20: private TaskScheduler taskScheduler;
21:
22: @Test
23: public void testScheduleWithTriggerEverySecond() throws InterruptedException {
24: taskScheduler.schedule(new Runnable() {
25:
26: @Override
27: public void run() {
28: log.debug("Run by thread: [{}]", Thread.currentThread().getName());
29: }
30: }, new CronTrigger("* * * * * *"));
31: Thread.sleep(5000);
32: }
33:
34: }
Poniżej przykład wykonania testu. Jak widać zadanie uruchamiane jest 5 razy co sekundę - na 5 sekund po zleceniu zadań wstrzymywany jest wątek główny.
0 [taskScheduler-1] DEBUG pl.com.mbsoftware.eiws.concurrency.TaskSchedulerTest - Run by thread: [taskScheduler-1]
1000 [taskScheduler-1] DEBUG pl.com.mbsoftware.eiws.concurrency.TaskSchedulerTest - Run by thread: [taskScheduler-1]
2000 [taskScheduler-1] DEBUG pl.com.mbsoftware.eiws.concurrency.TaskSchedulerTest - Run by thread: [taskScheduler-1]
3000 [taskScheduler-1] DEBUG pl.com.mbsoftware.eiws.concurrency.TaskSchedulerTest - Run by thread: [taskScheduler-1]
4000 [taskScheduler-1] DEBUG pl.com.mbsoftware.eiws.concurrency.TaskSchedulerTest - Run by thread: [taskScheduler-1]
Na koniec propozycja porównania możliwości planowania zadań, które daje nam Java z tymi udostępnianymi przez Spring.
Dla przypomnienia - podstawowym interfejsem w Javie (jeśli chodzi o planowanie zadań) jest ScheduledExecutorService, natomiast w Spring'u jest, omawiany w bieżącym wpisie, TaskScheduler.
Funkcjonalność | Java | Spring |
Zaplanowanie wykonania zadania na konkretny czas | + | + |
Zaplanowanie wykonania zadania za określony czas | + | + |
Wykonywanie zadania cyklicznie co określony czas pomiędzy uruchomieniami kolejnych wywołań | + | + |
Wykonywanie zadania cyklicznie co określony czas pomiędzy koniem przetwarzania poprzedniego zadania a początkiem przetwarzania następnego | + | + |
Możliwość zdefiniowania własnej strategii uruchamiania kolejnych wywołań zleconego zadania | - | + |
Ukrycie implementacji dostarczanych przez zewnętrzne providery pod spójnym interfejsem | - | + |
Pomimo tego, że Spring korzysta z natywnych elementów dostarczanych z Javą, udostępnia o wiele więcej funkcjonalności niż te standardy. Poza tym dzięki temu, że dostarcza spójny interfejs do różnych implementacji pozwala uzyskać o wiele większą elastyczność niż podstawowe implementacje.
Brak komentarzy:
Prześlij komentarz