SCJP, podejście dziewiąte

Przedostatni rozdział podręcznika dotyczy zagadnienia równie rozbudowanego i skomplikowanego jak kolekcje. Konkretnie chodzi o wątki – rzecz, której za bardzo nie można się nauczyć “chałupniczo”, gdyż prawdziwą potęgę wielowątkowej aplikacji widać w… no… wielowątkowej aplikacji 🙂 A takich raczej nikt nie klepie po godzinach ku radości własnej i pracy u podstaw.

Autorzy zaznaczają, że to, co przedstawili w tym rozdziale jest zaledwie wierzchołkiem góry lodowej jeśli chodzi o aplikacje wielowątkowe, zatem i nalezy podchodzić do zdobytej wiedzy ostrożnie. Jednakże i tak jest się czego nauczyć, jak i jest z czego stworzyć dziesiątki podchwytliwych pytań. Generalnie do pozytywnego uporania się z zadaniami potrzebne są dwie rzeczy – bliska znajomość z API wątków, konstruktorów oraz wywoływanych metod, wyrzucanych wyjątków i takich tam. Z drugiej zaś strony należy zrozumieć blokady (locki), aby wiedzieć kiedy i jaki wątek jest dostępny, uśpiony, ma możliwość odwołania się do wskazanego obiektu, czy co tam jeszcze w ogóle przyjdzie do głowy. Oto lista rzeczy, z którymi lepiej pozostawać w bliskiej komitywie.

  • usypianie wątku – o metodzie sleep trzeba wiedzieć dwie rzeczy. Raz – jest metodą statyczną klasy Thread. Dlatego też zawsze usypia działanie bieżącego wątku. Do tego wyrzuca ona InterruptedException, zatem musimy wywołanie metody zawrzeć w klauzuli try..catch, albo też przekazać obsługę wyjątku wyżej.
  • metoda join() – podobnie jak powyższa wyrzuca wyjątek InterruptedException. Nie jest jednakże metodą statyczą. Spójrzmy na taki kod:
    public static void main( String[] args ) {
    Thread t = new Thread( new Runnable() {
    public void run() {
    System.out.println( “Początek pętli” );
      for( double i = 0; i < 1000000000; i++ ) { } System.out.println( "Koniec pętli" ); } } ); System.out.println( "Chlebik 1:" ); t.start(); System.out.println( "Chlebik 2:" ); try { t.join(); } catch( Exception e ) { } System.out.println( "Chlebik 2:" ); } [/sourcecode] Obecnie wykonywany wątek (ten z metody main) nie wyświetli napisu Chlebik 2: dopóki nie zostanie zakończone działanie wątku reprezentowanego przez obiekt t!
  • metoda wait() – odstaje troszeczkę od powyższego towarzystwa, gdyż jest ona właściwa dla wszystkich obiektów w Javie (pochodzi z klasy Object). Nie jest statyczna, zaś jej wywołanie nie powoduje wyrzucenia wyjątku. W parze z nią idą dwie inne metody z klasy Object – notify() i notifyAll(). Wszystkie są oznaczone jako final, zatem nie potrzeba w ich przypadku karkołomnych zabaw jak z np. metodą equals.

    Idąc dalej – metody te mogą być wywołane tylko i wyłącznie w kontekście synchronized! Próby użycia poza tymże kontekstem skutkują wyrzuceniem IllegalMonitorStateException (i to nie jest sprawdzalny wyjątek więc nie trzeba definiować jego łapania). Metody te służą do zarządzania blokadami obiektu (dlatego są elementami klasy Object). Metoda wait pozwala na wstrzymanie działania wątku, który posiada blokadę obiektu, aż do wywołania przez ten obiekt metody notify, albo notifyAll. Nie za bardzo podejmuję się więcej tłumaczyć to pisząc – myślę, że kod powie więcej (wzięty z podręcznika):

    class ThreadA {
    public static void main(String [] args) {
    ThreadB b = new ThreadB();
    b.start();

    synchronized(b) {
    try {
    System.out.println(“Waiting for b to complete…”);
    b.wait();
    } catch (InterruptedException e) {}
    System.out.println(“Total is: ” + b.total);
    }
    }
    }

    class ThreadB extends Thread {
    int total;

    public void run() {
    synchronized(this) {
    for(int i=0;i<100;i++) { total += i; } notify(); } } } [/sourcecode]

  • kiedy blokada istnieje, a nie kiedy nie – to temat bardzo istotny. Oto cytat z Thinking in Java w wersji 4.

    Ważne jest, aby zrozumieć, że wywołanie metody sleep() nie zwalnia blokady obiektu, tak samo jak nie czyni tego wywołanie yield(). Z drugiej strony, wywołanie wait() zainicjowane w obrębie synchronizowanej metody wymusza zawieszenie wątku i zwolnienie blokady danego obiektu.

  • metoda getId() – na to się trochę wkurzyłem. Ni stąd, ni zowąd wyskoczyły mi pytania o tę metodę. I co? Jajco! (cytat ze znanego polskiego filmu). Może to i metoda, aby w pytaniach testowych poruszać zagadnienia, których nie było w konkretnym rozdziale. Ale to nie lepiej by było po prostu dać listę metod, o które potencjalnie jeszcze mogą paść pytania? Uczenie się całego API na pamięć to chyba nie jest cel egzaminacyjny? No nic, koniec narzekania – rzecz w metodzie getId().

    Dokumentacja mówi, że zwraca ona unikatowy identyfikator wątku (prymityw typu long). Spójrzmy na ten kod:

    // Obiekty a-a3 są tego samego typu jak ten poniżej

    Thread a4 = new Thread( new Runnable() {
    public void run() {
    for( int i = 0; i < 100000; i++ ) { if( i == 99999 ) System.out.println('.' + Thread.currentThread().getId()); }} } ); System.out.println( Thread.currentThread().getId() ); a.start(); System.out.println( a.getId() ); a2.start(); System.out.println( a2.getId() ); a3.start(); System.out.println( a3.getId() ); a4.start(); System.out.println( a4.getId() ); [/sourcecode] Oto efekt działania: 1
    8
    9
    10
    11
    54
    55
    56
    57

    I tak raz za razem – kolejność cyfr oczywiście bywa różna (poza pierwszymi trzema-czterema) – wiadomo, nieokreśloność wywoływania wątków. Tak to wygląda. Dwie kwestie – jak widać wątek metody main() ZAWSZE (przynajmniej u mnie) ma numer 1. Pozostałe – w miarę równo i oczywiście idąc w górę. Pytania na testowym egzaminie dotyczyły ewentualnego wyniku na wyjściu programu podobnego do powyższego. Ciekawe, ale jednak to w wielu momentach jest po prostu loteria.

Advertisements

One thought on “SCJP, podejście dziewiąte

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s