Timer w EJB 3.1 – wymagany tylko jeden wątek w tym samym czasie

Timer w EJB 3.1 – wymagany tylko jeden wątek w tym samym czasie

Podczas migracji z quartz’a na timer’y EJB można “nadziać” się na wymóg, który można było rozwiązać prostą implementacją interfejsu StatefulJob. Co zrobić, aby zmusić sheduler do czekania, jeśli nasza metoda jeszcze nie zakończyła działania?

Przede wszystkim bean, który wywołuje metodę powinien być singleton’em. Metody singleton’a są domyślnie blokowane (write-lock) dla kontenera, więc zostanie on powstrzymany przed powtórnym wywołaniem naszej metody, jeśli jakiś wątek jeszcze się w niej znajduje.

Jednak nawet w taki sposób zablokowany, kontener będzie próbował ponownie uruchomić metodę oznaczoną annotacją @Shedule (bądź inaczej ustawiony timer). Poniżej gotowy przykład jak poradzić sobie z długo czasem trwającymi metodami wywoływanymi nieco za często 😀 i towarzyszącymi im wyjątkami.

Tworzymy beana’ który będzie wywoływał metodę roboczą na innym bean’ie. Metodę, timeout annotujemy @Lock(javax.ejb.LockType.READ), aby wiele wątków mogło się do niej dostać (ale tylko jeden będzie mógł zmieniać wartości).

@Singleton
public class OurSheduleBean {

    @EJB
    private OurInterfacelessWorkerBean worker;

    @Lock(javax.ejb.LockType.READ)
    @Schedule(second = "*/2", minute = "*", hour = "*", persistent = false)
    public void timeout() {
        worker.doSmth();
    }
}

EJB 3.1 nie zezwala na przejście z read-lock na write-lock. Dlatego potrzebny jest oddzielny bean (worker). Utworzymy bean lokalny bez interfejsu (wreszcie można).

@Singleton
@LocalBean
public class OurInterfacelessWorkerBean {

    private AtomicBoolean busyFlag = new AtomicBoolean(false); //flaga zajetosci

    @Override
    public void doSmth() {
        if (!busyFlag.compareAndSet(false, true)) {
            return;
        }

        try {
            //nasza metoda
            Thread.sleep(20000); //baaardzo spiaca metoda
        } finally {
            //flaga w dol, mozna wykonywac
            busyFlag.set(false);
        }
    }
}

Worker wykorzystuje flagę zajętości typu AtomicBoolean (który zapewnia bezpieczne operacje w środowisku wielowątkowym). Jeśli tylko można (flaga = false), metoda ustawi flagę na true i wykona nasze długotrwałe operacje. Po wszystkim flaga znów wędruje w “dół”, aby zezwolić innym na wykorzystanie metody.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.