2012-08-14 12 views
9

Czasami powtarzane zadanie jest dłuższe niż jego okres (w moim przypadku może to się zdarzyć przez wiele godzin) . Pomyśl o powtarzającym się zadaniu, które trwa 7 minut i planowane jest uruchamianie co 10 minut, ale czasami trwa to 15 minut na każdy bieg przez kilka godzin z rzędu.Zaplanuj powtórzenie jednowątkowe powtarzalne w java, ale pomiń bieżący przebieg, jeśli poprzedni przebieg nie został zakończony.

Klasy Timer i ScheduledThreadPoolExecutor mają metodę scheduleAtFixedRate, która jest zwykle używana w tego typu funkcjach. Jednak obie mają tę cechę, że "starają się nadrobić zaległości, gdy za nimi się znajdują". Innymi słowy, jeśli czasomierz jest opóźniony o kilka egzekucji, buduje kolejkę pracy, która będzie pracowała nieprzerwanie, dopóki nie zwróci się do liczby serii, które miałyby miejsce, gdyby żadne z zadań nie zajęło więcej czasu określony czas. Chcę uniknąć tego zachowania, pomijając bieżące wykonanie, jeśli poprzednie uruchomienie nie zostało zakończone.

Mam jedno rozwiązanie, które wymaga rozmyślania za pomocą metody AfterExution z puli executorów, ponownego obliczania opóźnienia i zmiany harmonogramu działania z nowym opóźnieniem, ale zastanawiałem się, czy istnieje prostszy sposób, lub czy ta funkcja już istnieje w wspólna biblioteka gdzieś. Wiem o planowaniu z ustalonym opóźnieniem, a nie ustalonym okresem, ale to nie zadziała, ponieważ ważne jest, aby spróbować wykonać zadania w ustalonym czasie. Czy są jakieś prostsze opcje niż moje rozwiązanie AfterExecution?

+3

Najprostszym zadaniem jest sprawdzenie, czy jest już uruchomiona inna instancja, a jeśli tak, natychmiast zrób to sam. –

+0

@Hot Licks: ScheduledExecutorService nie uruchamia ich jednak równolegle. –

+0

Więc jaki jest tutaj podstawowy wymóg? Że to tylko i zawsze zaczyna się ** na ** 10-minutowym odstępie, niezależnie od tego, czy brakowało jednego? Jeśli tak, to nie mówisz o kursie. –

Odpowiedz

13

Myślę, że to, co chcesz, to samo długo działające zadanie, które nie działa w samej usłudze ScheduledExecutorService, ale w wątku w tle. Wtedy zadanie o stałej stawce zawsze będzie szybko wykonywane, ponieważ służy tylko do sprawdzania, czy uruchomić rzeczywiste zadanie w tle (lub nie, jeśli nadal działa od ostatniego razu).

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 
final Runnable actualTask = null; 

executorService.scheduleAtFixedRate(new Runnable() { 
    private final ExecutorService executor = Executors.newSingleThreadExecutor(); 
    private Future<?> lastExecution; 
    @Override 
    public void run() { 
     if (lastExecution != null && !lastExecution.isDone()) { 
      return; 
     } 
     lastExecution = executor.submit(actualTask); 
    } 
}, 10, 10, TimeUnit.MINUTES); 
+0

Wspomniałem już o tej opcji w moim pytaniu. – jonderry

+0

@jonderry: Przepraszam, brakowało twojego ostatniego zdania. Mam kilka innych pomysłów, nie jestem pewien, czy będą czystsze. –

+0

@jonderry: OK, myślę, że ostatnia sugestia, którą napisałem, jest dobra. Oczyściłem swoją odpowiedź, możesz zobaczyć historię edycji innych sugestii. –

1

Stwórz trzecią klasę, na przykład Koordynator. Koordynator ma zsynchronizowaną metodę startRunning(), która ustawia wartość isRunning na true i zwraca wartość true, jeśli inny wątek nie był już uruchomiony. Powinna również istnieć zsynchronizowana metoda stopRunning, która ustawia wartość isRunning na false. Zwraca wartość true, jeśli działa już. Tworzysz pojedynczą instancję tej klasy i przekazujesz odwołanie do wszystkich tworzonych runn. W metodzie runnable's run najpierw wywołujecie startRunning i sprawdzacie powrót, aby sprawdzić, czy inny już nie działa. Upewnij się, że wpisujesz kod w run() w try-finally i wywołuje stopRunning z wewnątrz bloku finally.

Powiązane problemy