piątek, 20 czerwca 2014

Wielowątkowość - współdzielenie obiektów

Widzialność

 

Jeśli nie zastosowana jest synchronizacja kompilator może zmienić kolejność wykonywania poleceń więc jeśli dwie zmienne są przypisywane jedna po drugiej - nie ma pewności że ich wartości będą widoczne w tym samym czasie dla innego wątku w tej samej kolejności. Możliwe jest widzenie starych wartości (stale data) a w przypadku zmiennych 64bitowych możliwe jest widzenie połowy wartości z dwóch wartości którą ta zmienna miała ponieważ ich zamiana jest robiona w dwóch krokach.

Jednym z rozwiązań jest stosowanie blokad - jeśli wątek uzyska blokadę następnie zmienni zmienną i zwolni blokadę, to inny wątek który uzyskał tą samą blokadę będzie widzieć wszystkie dane.

Innym przykładem są zmienne volatile. Jeśli jeden wątek A zapisuje do zmiennej volatile wartość a następnie inny wątek B ją czyta to zobaczy dobrą wartość zmiennej jak i innych wcześniej zmienianych zmiennych przez wątek A.

Przykład: Wątek Reader nie widzi zmiennej ready więc będzie działał w nieskończoność

public class NoVolatile {
    private static boolean ready = false;
       private static class Reader extends Thread {
        public void run() {
            int i = 0;
            while(!ready) {
                i++;
            }
        }
    }
     public static void main(String[] args) throws InterruptedException {
        new Reader().start();
        TimeUnit.SECONDS.sleep(1);
        ready = true;
    }
}


Po zastosowaniu słowa kluczowego volatile program się zakończy po sekundzie jak przewidywano:

public class VolatileExample {
    private static volatile boolean ready = false;
    private static class Reader extends Thread {
        public void run() {
            int i = 0;
            while(!ready) {
                i++;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        new Reader().start();
        TimeUnit.SECONDS.sleep(1);
        ready = true;
    }
}


Powiązanie z wątkiem - thread confinement

 

Jeśli dane (klasa) nie jest dostępna dla wielu wątków wtedy nie trzeba stosować synchronizacji ani tworzyć klas thread-safe. Przykładem jest Swing gdzie jest jeden wątek odpowiedzialny za obsługę eventów. Mechanizm ten jest mechanizmem który może być jedynie zaznaczony w dokumentacji - nie da się wymusić aby klasa była używana tylko w jednym wątku.

Przykłady powiązań z wątkiem:
  • zmienna volatile którą czyta wiele wątków ale zmienia tylko jeden
  • zmienna lokalna w metodzie - jest powiązana ze stosem wywołania
  • zmienna ThreadLocal - dla każdego wątku inna instancja

Niezmienność - Immutability

 

Klasy immutable są zawsze thread-safe. Mogą mieć tylko jeden stan który jest ustawiany przy tworzeniu klasy.
Klasa jest niezmienna (immutable) jeśli:
  • jej stan nie może być zmieniony po konstrukcji
  • wszystkie pola są final
  • jest poprawnie stworzona - jej referencja nie "ucieknie" podczas konstrukcji
Klasa (instancja) immutable  może być bezpiecznie publikowana nawet bez użycia synchronizacji! Wystarczy że została poprawnie stworzona.

Publikacja i ucieczka 

 

Publikacja to udostępnianie zmiennych na zewnątrz.
Ucieczka this jest możliwa np:

public class Escape {
    public Escape(EventSource eventSource) throws InterruptedException {
        eventSource.registerListener(new EventListener() {
            @Override
            public void onEvent() {
                someMethod();
            }
        });
    }
}


Aby bezpiecznie opublikować obiekt obie referencja obiektu jak i jego stan muszą być widoczne dla innych wątków  w tym samym czasie. Aby tego dokonać należy:
  • zainicjować obiekt ze statycznego inicjatora - inicjowanie przy deklarowaniu statycznego pola
  • trzymać jego zmienną w polu volatile albo AtomicReference
  • trzymać jego zmienną w polu final w poprawnie skonstruowanym obiekcie
  • trzymać jego zmienną w polu który jest poprawnie chronione przez blokadę ( tyczy się także kolekcji synchronizowanych albo innych bezpiecznych kolekcji jak ConcurentHashMap, Vector, BlockingQueue itp
Część obiektów można nazwać efektywnie niezmiennymi - czyli nie są one immutable ale po stworzeniu nie są zmieniane aby ich używać należy tylko je bezpiecznie publikować.
Jeśli chodzi o obiekty zmienne - muttable - to należy je bezpiecznie publikować jak i muszą być thread-safe albo muszą być chronione przez blokadę.
Jeśli chodzi o obiekty immutable - to nie muszą być one publikowane bezpiecznie - mogą być jakkolwiek - np przez pole public.


Brak komentarzy:

Prześlij komentarz