05 grudnia 2011

j.u.concurrent.Phaser i wątek onAdvance w Java 7

Każdorazowo, kiedy przygotowywałem klasę wspomagającą rozpoznanie java.util.concurrent.Phaser jej uruchomienie wymagało dodania opcji -ea w konfiguracji uruchomieniowej w Eclipse. W ten sposób mogłem prezentować moje doświadczenia w postaci pojedynczej klasy z użyciem assert. To jednak wymagało ode mnie właściwej konfiguracji dla każdej uruchamianej klasy (!) Wystarczy jednak mała zmiana w konfiguracji JRE i po sprawie!


Podczas mojego ostatniego wystąpienia o zmianach w Java 7 w kontekście programowania współbieżnego, w którym przedstawiłem j.u.c.Phaser, padło pytanie o wątek, który uruchamia metodę protected boolean onAdvance(int phase, int registeredParties). Za pomocą tej metody kontrolujemy dostępność obiektu Phaser. Kiedy zwróci true, Phaser kończy swoje działanie. Dzięki niej, mamy również możliwość uruchomienia akcji podsumowującej fazę, kiedy ostatni wątek w danej fazie zgłosi swoje przybycie, a przed przejściem do kolejnej (jeśli takowa w ogóle nastąpi).

Podczas spotkania nie byłem w stanie odpowiedzieć na pytanie o wątek, w którym będzie uruchomiona metoda onAdvance(), co czynię teraz, w poniższej klasie.
package pl.japila.java7.concurrent.phaser;

import java.util.concurrent.Phaser;

public class PhaserOnAdvanceDemo {

    static class MyPhaser extends Phaser {
        String threadName;

        public void setOnAdvanceThreadName(String threadName) {
            this.threadName = threadName;
        }

        protected boolean onAdvance(int phase, int parties) {
            assert threadName.equals(Thread.currentThread().getName());
            return true;
        }
    }

    public static void main(String[] args) throws Exception {

        final MyPhaser phaser = new MyPhaser();

        assert 0 == phaser.getPhase();
        assert 0 == phaser.getRegisteredParties();
        assert 0 == phaser.getUnarrivedParties();

        final int parties = 2;

        phaser.bulkRegister(parties); // register 2 threads - main's and one more

        assert 0 == phaser.getPhase();
        assert parties == phaser.getRegisteredParties();
        assert parties == phaser.getUnarrivedParties();

        phaser.arrive(); // main thread arrives

        assert 0 == phaser.getPhase();
        assert parties == phaser.getRegisteredParties();
        assert 1 == phaser.getUnarrivedParties();

        Thread t = new Thread() {
            public void run() {
                phaser.arrive(); // this (sub)thread arrives
            }
        };
        phaser.setOnAdvanceThreadName(t.getName());
        t.start();

        t.join(); // wait till t dies

        assert phaser.isTerminated(); // onAdvance returned true, and hence phaser is terminated
        assert 0 > phaser.getPhase(); // phaser is terminated, and hence getPhase() returns negative number
        assert parties == phaser.getRegisteredParties();
        assert 0 == phaser.getUnarrivedParties();
    }

}
Zanim uruchomisz powyższą klasę, zastanów się, jaka może być odpowiedź. Zmiana kolejności uruchomienia phaser.arrive() ułatwia znalezienie odpowiedzi. Wystarczy przenieść linię 36 z phaser.arrive() dla głównego wątku, po t.join() w linii 50. Potrafisz wytłumaczyć dlaczego? Chętnie odpowiem na wyraźną prośbę (jej brak będzie oznaką znajomości odpowiedzi). Dla mnie to teraz taaaaakie oczywiste, ale trzeba było widzieć moją minę na prezentacji! :)

p.s. W nadchodzący piątek, 9 grudnia, występuję w roli prelegenta na konferencji Cracow.mobi z tematem RESTful Android. Będzie to moje pierwsze, androidowe wystąpienie, a 45 minut na prezentację uważam jedynie za możliwość nakreślenia tematu i jestem zmuszony do potraktowania go wyłącznie slajdami. Sugestie odnośnie sposobu i zawartości prezentacji tematu mile widziane.