Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Programowanie obiektowe 2
Wątki
2
Java-wątki Proces – działający program, ma własną przestrzeń adresową
Wątki działają w ramach jednego procesu, mają wspólną pamięć. Nawet przy zwykłych programach mamy współbieżność: jeden wątek wykonuje metodę main(),drugi obsługuje zarządzanie pamięcią (GarbageCollection),inny odświeża ekran
3
Java-wątki Tworzenie wątku – sposób 1: piszemy klasę dziedziczącą po klasie Thread i przesłaniamy jej metodę run,czyli class MójWątek extends Thread { public void run(){ //działanie wątku }
4
Java-wątki Po czym tworzymy obiekt tej klasy, czyli wątek: MójWątek w = new MójWątek(); (Przykład 1)
5
Java-wątki Tworzenie wątku – sposób 2:
piszemy klasę implementującą interfejs Runnable (z metodą run), tworzymy jej obiekt i przekazujemy go konstruktorowi klasy Thread, czyli:
6
Java-wątki class MójWątek implements Runnable { public void run(){
//działanie wątku } MójWątek w = new MójWątek(); Thread watek = new Thread(w); (Przykład 2)
7
Java-wątki Interfejs Runnable jest bardzo prosty:
public interface Runnable { public void run(); } Drugi sposób tworzenia wątku jest szczególnie istotny, gdy klasa wątku dziedziczy już z innej klasy; ponieważ nie może dziedziczyć z dwóch klas, musi implementować interfejs Runnable.
8
Java-wątki Konstruktor klasy Thread jako argument bierze nazwę wątku lub obiekt (typu) Runnable. (Przykład 2,3) Konstruktory klasy Thread: Thread(), Thread(String name), Thread(Runnable target), Thread(Runnable target, String name)
9
Java-wątki Metoda run w klasie Thread: private Runnable target;
public void run(){ if(target!=null) target.run(); } Referencja target ustawiana jest w konstruktorze, jeśli zostanie przekazany parametr klasy implementującej Runnable
10
Java-wątki Gdy piszemy własną klasę dziedziczącą z Thread, ta postać metody run() jest przesłonięta naszą wersją. Metoda run zawiera główne obliczenie wątku, gdy się skończy, skończy się wątek. Jej uruchomienie (czyli uruchomienie wątku) następuje za pomocą metody start
11
Java-wątki Gdybyśmy napisali: w.run() metoda run wykonałaby
class Test { public static void main(String[] args){ MojWatek w = new MojWatek(); w.start(); } Gdybyśmy napisali: w.run() metoda run wykonałaby się, ale to nie byłby wątek. Metoda start oprócz wywołania run przygotowuje całe środowisko dla działania wątku
12
Java-wątki Deklaracja metody run jest ustalona w klasie Thread bądź w interfejsie Runnable, więc nie możemy przekazywać jej argumentów. Musi być ona bezargumentowa. Natomiast możemy przekazywać dane do wątku poprzez konstruktor lub pola klasy wątku. Przez pola klasy możemy też pobrać od wątku jakieś dane.
13
Java-wątki Wątek, nawet gdy się skończył, jako obiekt nadal istnieje, więc mamy dostęp do jego pól. (Przykład 4) Nazwę wątkowi możemy nadać albo w konstruktorze, albo metodą void setName(String n) Metoda String getName() zwraca nazwę wątku (Przykład 5)
14
Java-wątki Wątek kończy się wraz z zakończeniem działania metody run(). Aby móc kontrolować zakończenie wątku z poziomu innego wątku, najlepiej zrobić to programowo, np. metoda run działając obserwuje wartość jakiegoś pola klasy i w przypadku zmiany tej wartości kończy działanie. (Przykład 6)
15
Java-wątki W starszych wersjach Javy istniała w klasie Thread metoda stop(),wymuszająca zakończenie wątku. Obecnie się jej nie używa.
16
Java-wątki W tym samym czasie działać może wiele niezależnych wątków
(Przykład 7) Wątki też mogą współdziałać przy wykonywaniu jakiejś pracy (Przykład 8) Mogą korzystać z tych samych obiektów (Przykład 9)
17
Java-wątki W przykładzie 9 mogą wystąpić błędy. Np. aktualny stan napisu to „abbaaba”. Wątek A wywołuje metodę doklej, chcąc dokleić „a”. Zanim to zrobi, wątek B też wywołuje metodę doklej, chcąc dokleić „b”. Wątek A dokleja „a” i wypisuje „abbaabaa”, wątek B dokleja „b” i wypisuje „abbaabab”.
18
Java-wątki Rozwiązaniem tej i podobnych sytuacji jest synchronizacja. Tylko jeden wątek w tym samym czasie ma prawo wywołać metodę doklej. Inny przykład tego samego problemu (Przykład 10) Wątki współdzielące te same zasoby mogą powodować niespójność danych.
19
Java-wątki Testowanie programów wielowątkowych jest trudne, bowiem możemy wiele razy otrzymać wyniki, które wydają się świadczyć o poprawności programu, a przy kolejnym uruchomieniu okaże się, że wynik jest nieprawidłowy. Wyniki uruchamiania programów wielowątkowych mogą być także różne na różnych platformach systemowych.
20
Java-wątki Synchronizacja jest mechanizmem, który zapewnia, że kilka wykonujących się wątków nie będzie równocześnie działać na tym samym obiekcie (czy pamięci wspólnej) nie będzie równocześnie wykonywać tego samego kodu.
21
Java-wątki Kod, który może być wykonywany w danym momencie tylko przez jeden wątek , nazywa się sekcją krytyczną. W Javie sekcje krytyczne wprowadza się jako bloki lub metody synchronizowalne.
22
Java-wątki Blokowanie obiektów jest sterowane słowem kluczowym synchronized.
23
Java-wątki Synchronizacja w Javie może być wykonana na poziomie
metod – słowo kluczowe synchronized występuje przy definiowaniu metody: public synchronized int balance() { … } (Przykład 11)
24
Java-wątki b) na poziome instrukcji: słowo kluczowe synchronized występuje przy definiowaniu bloku instrukcji (Przykład 12)
25
Java-wątki Kiedy wątek wywołuje metodę synchronizowaną, zamykany jest monitor (obiekt jest zajmowany przez wątek). Inne wątki usiłujące wywołać na rzecz tego obiektu metodę synchronizowaną (niekoniecznie tą samą) lub usiłujące wywołać instrukcję synchronized z podaną referencją do zajętego obiektu są blokowane i czekają na zakończenie wykonywania metody lub instrukcji synchronized przez wątek który zajął obiekt (zamknął monitor).
26
Java-wątki Dowolne zakończenie wykonywania metody synchronizowanej lub instrukcji synchronized zwalnia monitor dając czekającym wątkom możliwość dostępu do obiektu.
27
Java-wątki Wątki działają asynchronicznie. (Przykład 13, 13a)
Do koordynacji ich działania służą metody wait(), notify(), notifyAll() wait() zawiesza działanie wątku, aż inny nie wywoła notify() lub notifyAll() wait() można wywołać tylko będąc w metodzie bądź sekcji synchronized. Wykonanie wait() spowoduje zwolnienie monitora. (Przykład 14)
28
Java-wątki Dlaczego przykład 14 niepoprawny, chociaż metody są synchronized? (Przykład 15) Wymienione metody są metodami klasy Object, więc wątki mogą je wywoływać w dowolnych obiektach. notify() powoduje odblokowanie jednego z wątków, które wywołały wait()
29
Java-wątki Nie wiadomo, który wątek zostanie odblokowany. Wiadomo że jeden. notifyAll() odblokuje wszystkie
30
Java-wątki Obie te metody mogą być wywoływane tylko przez wątki zajmujące monitor. Odblokowany przez notify() wątek nie będzie wykonywany natychmiast – musi poczekać na wątek, który go zwolnił, aż ten opuści monitor. Po zwolnieniu monitora odblokowany wątek będzie konkurował z innymi (niezablokowanymi) o przydział do monitora.
31
Java-wątki Podobne uwagi dotyczą do notifyAll()
Problem producenta-konsumenta – przykład koordynacji wątków. Mamy dwie kategorie wątków: producenci i konsumenci. Wątki korzystają ze wspólnego bufora, w danej chwili tylko jeden może z niego korzystać. Producenci wkładają do bufora liczby, gdy jest on pusty. Gdy bufor jest pełny producenci muszą czekać na opróżnienie bufora przez konsumenta.
32
Java-wątki Konsumenci pobierają liczbę. Jeśli bufor jest już pusty, konsumenci muszą czekać, aż jakiś producent go napełni. (Przykład 16a, 16b)
33
Java-wątki Opisane wcześniej metody synchronizacji (synchronized, wait, notify itd.) są niskopoziomowe i trudne w użyciu, należy ich unikać. Wiele problemów synchronizacyjnych może być wyrażonych za pomocą kolejek blokujących. problem producenta-konsumenta jest tu dobrym przykładem.
34
Java-wątki Kolejka blokująca powoduje zablokowanie wątku podczas próby dodania elementu, jeśli jest pełna, lub podczas próby usunięcia elementu jeśli jest pusta. Sama kolejka oraz jej metody zapewniają synchronizację wątków i jest ona ukryta przez programistą.
35
Java-wątki Podstawowe metody kolejki blokującej:
put – dodaje element; blokuje, jeśli kolejka jest pełna take – usuwa i zwraca element z czoła; blokuje, jeśli kolejka jest pusta
36
Java-wątki W pakiecie java.util.concurrent znajduje się kilka wersji kolejek blokujących Kolejka LinkedBlockingQueue domyślnie nie posiada ograniczenia, ale można je ustawić.
37
Java-wątki Kolejka ArrayBlockingQueue ma określoną pojemność i opcjonalny parametr włączający wymóg uczciwości (tj. preferencyjnie dopuszcza te wątki, które czekają najdłużej). Z opcji uczciwości należy korzystać ostrożnie, gdyż może ona znacznie spowolnić działanie programu.
38
Java-wątki Problem producenta-konsumenta z buforem o pojemności 5 rozwiązany z użyciem kolejek blokujących (Przykład 21)
39
Java-wątki Kolekcje nie są bezpieczne wątkowo. Mogą zostać uszkodzone, gdy np. jeden wątek zostaje wywłaszczony w trakcie wstawiania bądź usuwania elementów, zanim dokończył przekierowywania łączy. Dlatego dostęp do kolekcji, z której korzysta wiele wątków, musi być synchronizowany
40
Java-wątki public class ArrayTest { private ArrayList a = new ArrayList(); public void dodaj(Object p, Object d){ synchronized(a){ a.add(p); a.add(d); } public Object get(int nr){ synchronized(a) { return a.get(nr); }
41
Java-wątki W pakiecie java.util.concurrent znajdują się implementacje map, zbiorów i kolejek, które są całkowicie bezpieczne i specjalnie przeznaczone do pracy w środowisku wielowątkowym: Mapa ConcurrentHashMap Metoda putIfAbsent dodaje nową parę kluczy, jeśli wcześniej ich nie było Metoda remove usuwa parę kluczy (jeśli są dostępne) Obie te metody działają niepodzielnie (Przykład 22)
42
Java-wątki Zbiór ConcurrentSkipListSet:
ConcurrentSkipListSet<E> ls = new ConcurrentSkipListSet<E>(); Gdzie E jest klasą implementującą interfejs Comparable (ponieważ ta kolekcja jest posortowana) Mapa ConcurrentSkipListMap, i jeszcze kilka innych kolekcji
43
Java-wątki Wszystkie te kolekcje zwracają słabo spójne iteratory, tzn. nie muszą one odzwierciedlać wszystkich wstawień elementów po ich utworzeniu. W przeciwieństwie do iteratorów zwykłych kolekcji, słabo sp. It. nie zgłaszają wyjątku ConcurrentModificationException, jeśli kolekcja zostanie zmodyfikowana po ich utworzeniu.
44
Java-wątki Kolekcja ConcurrentHashMap może obsłużyć 16 wątków zapisujących działających w tym samym czasie; jeśli ich jest więcej, nadmiar jest tymczasowo blokowany
45
Java-wątki Istnieje interfejs Callable, podobny do Runnable, posiadający metodę zwracającą wartość (w przeciwieństwie do run) public interface Callable<V> { V call() throws Exception; } Jest to typ parametryzowany
46
Java-wątki Np. obiekty klasy implementującej Callable<Integer> reprezentują wątki, których wynikiem jest obiekt typu Integer. Istnieje też specjalny interfejs Future, służący do przechowywania wyników obliczeń takich wątków.
47
Java-wątki public interface Future<V> { V get() throws… void cancel(boolean mayInterrupt); boolean isCancelled(); boolean isDone(); }
48
Java-wątki Metody obiektów typu Future działają na wątkach typu Callable – czekają na zakończenie ich obliczeń i pobierają od nich wyniki. Metoda get() – jej wywołanie jest zablokowane do zakończenia obliczeń. Druga wersja get – zgłasza wyjątek TimeoutException, jeżeli wątek nie zakończy się przed upływem określonego w argumencie czasu.
49
Java-wątki Obie metody get wyrzucają wyjątek InterruptedException jeśli wątek na którego obliczenia oczekują zostanie przerwany. Kiedy wątek skończy obliczenia, get natychmiast zwraca odebrany od niego wynik. isDone()-zwraca false jeśli obliczenia wątku jeszcze trwają, true – w p. p.
50
Java-wątki cancel(boolean myInterrupt) przerywa wątek, jeśli myInterrupt = true. Jeśli wątek jeszcze nie wystartował, zostanie anulowany. Popularną implementacją Future jest klasa FutureTask Scenariusz użycia:
51
Java-wątki Callable<Integer> watek = new… tu potrzebna klasa implementująca Callable<Integer> FutureTask<Integer> task = new FutureTask<Integer>(watek); Thread t = new Thread(task);//ok., bo FutureTask implementuje także Runnable t.start(); Integer wynik = task.get();//ok., bo FutureTask implementuje Future
52
Java-wątki Ilustracja do powyższego – Przykład 23 – program obliczający ilość wystąpień zadanego słowa we wszystkich plikach podanego katalogu (i jego podkatalogach)
53
Java-wątki Ilość wątków działających jednocześnie nie powinna być zbyt duża – może to spowodować znaczne spowolnienie działania a nawet awarię JVM. Problem ten rozwiązują pule wątków. Pula wątków to pewna liczba (ograniczona) wątków nieaktywnych czekających na zadania. Przekazujemy im obiekt typu Runnable, jeden z wątków puli go wykonuje, po czym nie kończy się, lecz czeka na kolejne zadania.
54
Java-wątki Pule wątków tworzymy statycznymi metodami fabrycznymi klasy Executors. Podstawowe trzy: newCachedThreadPool() – tworzy pulę do wykonywania wielu krótkotrwałych zadań. Wątki z tej puli, które już wykonały zadanie, czekają na kolejne (maks.60s);gdy zabraknie wątków do zadań, dynamicznie tworzone są nowe.
55
Java-wątki newFixedThreadPool – tworzy pulę o ustalonej liczbie wątków. Nieaktywne wątki są zachowywane bezterminowo. newSingleThreadExecutor – pula złożona z jednego wątku wykonującego zadania po kolei Wszystkie te metody zwracają pulę wątków – obiekt klasy ThreadPoolExecutor, implementującej interfejs ExecutorService
56
Java-wątki Interfejs ten zawiera metodę submit, służącą do przekazywania do puli wątków zadań: Future<?> submit(Runnable task) – przekazuje obiekt task do wykonania przez pulę i zwraca obiekt Future, dzięki któremu można kontrolować status wykonania naszego zadania (isDone, cancel, isCancelled); metoda get po zakończniu zadania zwraca null (bo task nie zwraca żadnej wartości, nie jest typu Callable)
57
Java-wątki Future<T> submit(Callable task) – przekazuje do wykonania obiekt typu Callable, zwraca obiekt typu Future, na którym możemy wywołać get i czekać, aż zwróci ona wynik działania wątku task Po zakończeniu działania puli wywołujemy shutdown() która zamyka pulę wątków. Zadania które czekają jeszcze w kolejce (bo było w puli za mało wątków by je od razu wykonać) będą jeszcze (po wywołaniu shutdown) wykonane, jednak żadne nowe nie są już przyjmowane.
58
Java-wątki Scenariusz użycia puli wątków:
Wywołanie statycznej metody newCachedThreadPool lub newFixedThreadPool z klasy Executors Przekazanie obiektu Runnable lub Callable za pomocą submit. Korzystanie ze zwróconego obiektu Future: anulowanie zadania, odebranie wyniku itp. shutdown
59
Java-wątki Scenariusz z przykładu 24: mamy klasę MatchCounter typu Callable. ExecutorService pool = Executors.newCachedThreadPool(); MatchCounter<String> counter = new MatchCounter<String>(…); Future<Integer> result = pool.submit(counter); Integer w = result.get(); pool.shutdown()
60
Java-wątki Interfejs ExecutorService posiada metodę invokeAny, która przekazuje do puli wszystkie obiekty z kolekcji typu Callable i zwraca wynik ukończonego zadania (nie wiadomo którego, prawdopodobnie pierwszego ukończonego): List<Callable<String>> zadania = new … ExecutorService pula = Executors.newCachedThreadPool(); String wynik = pula.invokeAny(zadania);
61
Java-wątki invokeAny stosuje się przy zadaniach typu rozkład liczby na czynniki pierwsze (łamanie szyfru RSA): wątki z kolekcji próbują rozkładu przy użyciu liczb z innego zakresu; kiedy którykolwiek z nich da odpowiedź, dalsze obliczenia można zatrzymać Metoda invokeAll przesyła wszystkie obiekty Callable z kolekcji i zwraca listę obiektów Future, dzięki którym możemy odebrać wyniki obliczone przez wątki.
62
Java-wątki List<Callable<T>> zadania = new … ExecutorService pula = Executors.newCachedThreadPool(); List<Future<T>> wyniki = pula.invokeAll(zadania); for(Future<T> wsk : wyniki) System.out.println(wsk.get());
63
Java-wątki Wadą powyższego rozwiązania jest to, że jeśli pierwszy wątek z listy działa długo, niepotrzebnie na niego czekamy, choć w tym czasie moglibyśmy odebrać wyniki od wielu wcześniej od niego zakończonych wątków. Rozwiązaniem tego problemu jest użycie klasy ExecutorCompletionService Tworzymy obiekt tej klasy, przekazując do konstruktora pulę wątków. Obiekt ten zarządza kolejką blokującą zawierającą obiekty typu Future, w których przechowywane są wyniki obliczeń. Powyższy przykład będzie wyglądał następująco:
64
Java-wątki List<Callable<T>> zadania = new …
ExecutorService pula = Executors.newCachedThreadPool(); ExecutorCompletionService ecs = new ExecutorCompletionService(pula); for(Callable<T> z:zadania) ecs.submit(z); for(int i = 0;i < zadania.size();i++) wykonaj(ecs.take().get());
65
Przykłady Przykłady-wątki.rar
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.