Równolegªo± w Javie w tki. Robert A. Kªopotek r.klopotek@uksw.edu.pl Wydziaª Matematyczno-Przyrodniczy. Szkoªa Nauk cisªych, UKSW 20.04.2017
Wielow tkowo± w Javie Równolegªo± w Javie oparta jest na w tkach a nie na procesach W tek Java odpowiada w tkowi systemu operacyjnego (o ile system operacyjny obsªuguje w tki) W tek to tzw. lekki proces (ró»nice w Windows i Linux): W tki wspóªdziel wspóln przestrze«adresow Przeª czanie kontekstu pomi dzy w tkami jest znacznie szybsze ni» pomi dzy procesorami W tki nie blokuj u»ytkownika (np. GUI jest na oddzielnym w tku) W tki mog wykonywa zadania równolegle i si synchronizowa W tki s niezale»ne, wi c je±li w jednym z w tków wyst pi wyj tek to nie ma on wpªywu na pozostaªe (poza Error) 2 / 36
Wielow tkowo± vs Wieloprocesowo± 1 Ka»dy proces ma wªasn przestrze«adresow i alokuje pomi tylko na wªasny u»ytek 2 Procesy s ci»kie (heavyweight) 3 Koszt komunikacji mi dzyprocesowej jest du»y (poprzez specjalne obiekty zarz dzane przez system operacyjny) 4 Przeª czanie pomi dzy procesami wymaga czasu na zapisania i zaªadowanie rejestrów, map pami ci, aktualizacji list zasobów itp. 1 W tki dziel t sam pami 2 w tki s lekkie (lightweight) 3 Koszt komunikacji mi dzyw tkowej jest maªy 4 Przeª czanie kontekstu pomi dzy w tkami jest szybkie, poniewa» w tki s wykonywane wewn trz procesu 3 / 36
Cykl»ycia w tku 4 / 36
Tworzenie w tku W tki mo»na utworzy na 2 sposoby: poprzez rozszerzenie klasy Thread poprzez implementacj interfejsu Runnable Konstruktory klasy Thread: Thread() Thread(String name) Thread(Runnable r) Thread(Runnable r,string name) 5 / 36
Metody klasy Thread (1/2) public void run() public void start() public void sleep(long miliseconds) public void join() public void join(long miliseconds) public int getpriority() public int setpriority(int priority) public String getname() public void setname(string name) public Thread currentthread() 6 / 36
Metody klasy Thread (2/2) public boolean isalive() public void yield() public void suspend() [depricated]. public void resume() [depricated]. public void stop() [depricated]. public boolean isdaemon() public void setdaemon(boolean b) public void interrupt() public boolean isinterrupted() public static boolean interrupted() 7 / 36
Thread i Runnable - przykªad class MultiT extends Thread { public void run (){ System. out. println ( " W tek dziaªa... " ); public static void main ( String args []){ MultiT t1= new MultiT (); t1. start (); class MultiR implements Runnable { public void run (){ System. out. println ( " W tek dziaªa... " ); public static void main ( String args []){ MultiR m1= new MultiR (); Thread t1 = new Thread ( m1 ); t1. start (); 8 / 36
Klasa anonimowa w tku - przykªad class TestMultitaskingT { public static void main ( String args []){ Thread t1= new Thread (){ public void run (){ System. out. println ( " task one " ); ; Thread t2= new Thread (){ public void run (){ System. out. println ( " task two " ); ; t1. start (); t2. start (); 9 / 36 class TestMultitaskingR { public static void main ( String args []){ Runnable r1= new Runnable (){ public void run (){ System. out. println ( " task one " ); ; Runnable r2= new Runnable (){ public void run (){ System. out. println ( " task two " ); ; Thread t1= new Thread ( r1 ); Thread t2= new Thread ( r2 ); t1. start (); t2. start ();
Thread Scheduler Thread Scheduler (planista w tków) jest cz ±ci JVM i decyduje, który w tek b dzie uruchomiony Nie ma»adnych gwarancji, który w tek w stanie "runnable" zostanie wybrany do uruchomiania przez planist w tków Tylko jeden w tek mo»e w tym samym czasie by uruchomiony przez proces Planista w tków wskazuje, które zadanie (w tek) b dzie wykonane jako nast pne na podstawie priorytetu i innych czynników (np. dotychczasowego czasu dziaªania) 10 / 36
Harmonogram wykonania Planista w tków wykorzystuje gªównie harmonogram planowania prewencyjnego (preemptive) lub interwaªy czasu (time slice), aby zaplanowa czas wykonania w tków: planowanie prewencyjne - zadanie (w tek) z najwy»szym priorytetem jest wykonywane dopóki nie zacznie czeka (wait) lub si nie zako«czy lub nie przyjdzie zadanie z wy»szym priorytetem interwaªy czasu - ka»dy w tek ma z góry okre±lony czas, w którym mo»e dziaªa, je±li nie sko«czy zostaje przeniesiony do puli w tków gotowych do uruchomienia i uruchamiany jest kolejny gotowy 11 / 36
Metoda Sleep - przykªad class TestSleepMethod extends Thread { public void run (){ for ( int i =1; i <5; i ++){ try { Thread. sleep (500); catch ( InterruptedException e ){ System. out. println (e ); System. out. println (i ); public static void main ( String args []){ TestSleepMethod t1= new TestSleepMethod (); TestSleepMethod t2= new TestSleepMethod (); t1. start (); t2. start (); 12 / 36
Wystartowanie 2 razy tego samego w tku - przykªad public class TestThreadTwice extends Thread { public void run (){ System. out. println (" running... " ); public static void main ( String args []){ TestThreadTwice t1= new TestThreadTwice (); t1. start (); t1. start (); // running... // Exception in thread " main " // java. lang. IllegalThreadStateException 13 / 36
Czekanie na w tki - przykªad public class TestJoinMethod extends Thread { public void run (){ for ( int i =1; i <=5; i ++){ try { Thread. sleep (500); catch ( Exception e ){ System. out. println (e ); System. out. println ( Thread. currentthread (). getname ()+ " : "+i ); public static void main ( String args []){ TestJoinMethod t1= new TestJoinMethod (); TestJoinMethod t2= new TestJoinMethod (); TestJoinMethod t3= new TestJoinMethod (); System. out. println ( Thread. currentthread (). getname ()); t1. start (); try { t1. join (1500); catch ( Exception e ){ System. out. println (e ); t2. start (); t3. start (); 14 / 36
Czekanie na w tki - wydruk main Thread -0 : 1 Thread -0 : 2 Thread -0 : 3 Thread -1 : 1 Thread -2 : 1 Thread -0 : 4 Thread -1 : 2 Thread -2 : 2 Thread -0 : 5 Thread -1 : 3 Thread -2 : 3 Thread -1 : 4 Thread -2 : 4 Thread -2 : 5 Thread -1 : 5 15 / 36
Ustawianie priorytetów ka»dy w tek ma priorytet priorytet jest reprezentowany przez liczb z zakresu 1 do 10, priorytet domy±lny to 5 w wi kszo±ci przypadków planista ustawia w tki wg priorytetu, ale nie jest to gwarantowane i zale»y od stanu JVM 3 staªe w klasie Thread public static int MIN_PRIORITY public static int NORM_PRIORITY public static int MAX_PRIORITY 16 / 36
W tki - demony Zadaniem w tków-demonów (daemon thread) jest dostarczenie usªug dla w tków u»ytkownika»ycie tych w tków zale»y od ªaski w tków u»ytkownika - je±li wszystkie w tki u»ytkownika zako«cza dziaªanie JVM automatycznie zabija wszystkie w tki-demony w tki-demony dziaªaj w tle i maja niski priorytet mo»emy ustawi czy w tek jest demonem tylko przed uruchomieniem - ustawianie po uruchomieniu zako«czy si wyj tkiem IllegalThreadStateException 17 / 36
Watki - demony public class TestDaemonThread extends Thread { public void run (){ if ( Thread. currentthread (). isdaemon ()){ // sprawdzanie czy to w tek - demon System. out. println (" daemon thread work " ); else { System. out. println (" user thread work " ); public static void main ( String [] args ){ TestDaemonThread t1= new TestDaemonThread (); TestDaemonThread t2= new TestDaemonThread (); TestDaemonThread t3= new TestDaemonThread (); t1. setdaemon ( true ); // teraz w tek t1 jest w tkiem - demonem t1. start (); t2. start (); t3. start (); 18 / 36
Pula w tków Pula w tków w Javie to grupa w tków, które czekaj na zadanie i mo»na je wielokrotnie wykorzystywa Pula w tków na z góry ograniczona pojemno± - ilo± w tków Watek jest wyci gany z puli, aby wykona zadanie, a jak ju» je sko«czy to jest z powrotem wkªadany do puli w tków oczekuj cych Zaleta puli w tków jest jej wi ksza wydajno±, poniewa» nie ma potrzeby na nowo tworzenia obiektów w tku Powszechne zastosowanie ma w Servletach i JSP (JavaServer Pages), gdzie kontener tworzy pule w tków od obsªugi zapyta«serwera Za obsªug puli w tków odpowiadaj klasy ExecutorService i Executors 19 / 36
Pula w tków - przykªad import java.util. concurrent. ExecutorService ; import java.util. concurrent. Executors ; class WorkerThread implements Runnable { private String message ; public WorkerThread ( String s ){ this. message = s ; public void run () { System.out. println ( Thread. currentthread (). getname () + " ( Start ) message = "+ message ); processmessage (); System.out. println ( Thread. currentthread (). getname () + " ( End ) " ); private void processmessage () { try { Thread. sleep (2000); catch ( InterruptedException e ) { e. printstacktrace (); public class TestThreadPool { public static void main ( String [] args ) { ExecutorService executor = Executors. newfixedthreadpool (5); for ( int i = 0; i < 10; i ++) { Runnable worker = new WorkerThread ( " " + i ); executor. execute ( worker ); executor. shutdown (); while (! executor. isterminated ()) { System.out. println ( " Koniec w tków " ); 20 / 36
Pula w tków - wydruk pool -1 - thread -1 ( Start ) message = 0 pool -1 - thread -3 ( Start ) message = 2 pool -1 - thread -2 ( Start ) message = 1 pool -1 - thread -4 ( Start ) message = 3 pool -1 - thread -5 ( Start ) message = 4 pool -1 - thread -2 ( End ) pool -1 - thread -3 ( End ) pool -1 - thread -1 ( End ) pool -1 - thread -1 ( Start ) message = 7 pool -1 - thread -3 ( Start ) message = 6 pool -1 - thread -2 ( Start ) message = 5 pool -1 - thread -5 ( End ) pool -1 - thread -4 ( End ) pool -1 - thread -4 ( Start ) message = 9 pool -1 - thread -5 ( Start ) message = 8 pool -1 - thread -2 ( End ) pool -1 - thread -1 ( End ) pool -1 - thread -3 ( End ) pool -1 - thread -4 ( End ) pool -1 - thread -5 ( End ) Koniec w tków 21 / 36
Grupy w tków W tki w Javie mo»na poª czy w grupy, aby jednocze±nie wykonywa operacje na caªej grupie a nie tylko na pojedynczych w tkach UWAGA: metody suspend(), resume() i stop() s oznaczone jako "deprecated" Za grup w tków odpowiada klasa ThreadGroup: ThreadGroup(String name) ThreadGroup(ThreadGroup parent, String name) Thread(ThreadGroup group, Runnable target, String name) int activecount() int activegroupcount() void destroy() ThreadGroup getparent() void interrupt() void list() 22 / 36
Grupy w tków - przykªad public class ThreadGroupDemo implements Runnable { public void run () { System. out. println ( Thread. currentthread (). getname ()); public static void main ( String [] args ) { ThreadGroupDemo runnable = new ThreadGroupDemo (); ThreadGroup tg1 = new ThreadGroup (" Parent ThreadGroup " ); Thread t1 = new Thread ( tg1, runnable," one " ); t1. start (); Thread t2 = new Thread ( tg1, runnable," two " ); t2. start (); Thread t3 = new Thread ( tg1, runnable," three " ); t3. start (); System. out. println (" Thread Group Name : "+ tg1. getname ()); tg1. list (); 23 / 36
Java Shutdown Hook Hak zamkni cia (shutdown hook) mo»e by u»yty do przeprowadzania czyszczenia zasobów lub zapisywania stanu, gdy JVM wyª cza si normalnie lub nagle Je±li chcemy wykona jakie± czyszczenie zasobów zanim JVM si zamknie, to powinni±my u»y haka zamykania Kiedy JVM si zamknie: u»ytkownik nacisn ª CTRL+C zostaªa wywoªana metoda System.exit(int) u»ytkownik si wylogowaª u»ytkownik zamyka system 24 / 36
Shutdown Hook - przykªad class MyThread extends Thread { public void run (){ System. out. println (" Shut down hook task completed." ); public class TestShutdown { public static void main ( String [] args ) throws Exception { Runtime r= Runtime. getruntime (); r. addshutdownhook ( new MyThread ()); System. out. println (" Now main sleeping. Press CTRL +C to exit." ); try { Thread. sleep (3000); catch ( Exception e) { 25 / 36
Synchronizacja w Javie Synchronizacja to zdolno± do kontrolowania dost pu wielu w tków do jakiegokolwiek dzielonego zasobu Synchronizacja jest gªównie u»ywana do zapobiegania ingerencji w tków mi dzy sob oraz do zachowania spójno±ci zasobów i wykonania S 2 gªówne sposoby na synchronizacj : Wzajemne wykluczanie - poprzez metody synchronized nazwa_metody {, poprzez bloki synchronizowane synchronized(object reference expression) { oraz statyczn synchronizacj Kooperacja - komunikacja mi dzyw tkowa w Javie Koncepcja blokady (lock) - gdy jeden z w tków uzyska dost p do zasobu synchronizowanego to zakªada na niego blokad i inne w tki juz nie maj do niego dost pu 26 / 36
Synchronizacja metody - przykªad class Table { synchronized void printtable ( int n ){ // metoda synchronizowana for ( int i =1; i <=5; i ++){ System. out. println (n*i ); try { Thread. sleep (400); catch ( Exception e ){ System. out. println (e ); public class TestSynchronization { public static void main ( String args []){ final Table obj = new Table (); // tylko jedne obiekt Thread t1= new Thread (){ public void run (){ obj. printtable (5); ; Thread t2= new Thread (){ public void run (){ obj. printtable (100); ; t1. start (); t2. start (); 27 / 36
Synchronizacja bloku - przykªad class Table { void printtable ( int n ){ synchronized ( this ){ // synchronizowany blok for ( int i =1; i <=5; i ++){ System. out. println (n*i ); try { Thread. sleep (400); catch ( Exception e ){ System. out. println (e ); public class TestSynchronization { public static void main ( String args []){ final Table obj = new Table (); // tylko jedne obiekt Thread t1= new Thread (){ public void run (){ obj. printtable (5); ; Thread t2= new Thread (){ public void run (){ obj. printtable (100); ; t1. start (); t2. start (); 28 / 36
Synchronizacja statyczna Je±li stworzymy statyczn metod zsynchronizowan, blokada znajdzie si w klasie nie na obiekcie! Problemy bez synchronizacji statycznej: 29 / 36
Synchronizacja statyczna - przykªad class Table { synchronized static void printtable1 ( int n ){ for ( int i =1; i <=5; i ++){ System. out. println (n*i ); try { Thread. sleep (400); catch ( Exception e ){ System. out. println (e ); void printtable2 ( int n ){ synchronized ( Table. class ){ // blokada klasy for ( int i =1; i <=5; i ++){ System. out. println (n*i ); try { Thread. sleep (400); catch ( Exception e ){ System. out. println (e ); 30 / 36
Problem zakleszczenia Zakleszczenia s cz ±ci problemów z równolegªo±ci Zakleszczenie to zbiór procesów b d cych w impasie wywoªanym przez to,»e ka»dy proces nale» cy do tego zbioru przetrzymuje zasoby potrzebne innym procesom z tego zbioru, a jednocze±nie czeka na zasoby przydzielone innym procesom. Jak prosto doprowadzi do zakleszczenia: w tek 1 blokuje zasób 1 i chce zablokowa zasób 2 potrzebny do zako«czenia zadania równolegle w tek 2 blokuje zasób 2 i chce zablokowa zasób 1 potrzebny do zako«czenia zadania w tki 1 i 2 b d czekaªy w niesko«czono±, a» który± zwolni potrzeby zasób do zako«czenia zadania 31 / 36
Zakleszczenie - przykªad public class TestDeadlockExample { public static void main ( String [] args ) { final String resource1 = " res1 " ; final String resource2 = " res2 " ; // w tek 1 blokuje zasób 1 a potem zasób 2 Thread t1 = new Thread () { public void run () { synchronized ( resource1 ) { System.out. println ( " Thread 1: locked resource 1 " ); try { Thread. sleep (100); catch ( Exception e ) { synchronized ( resource2 ) { System.out. println ( " Thread 1: locked resource 2 " ); ; // reszta main w drugiej kolumnie public class TestDeadlockExample { public static void main ( String [] args ) { // w tek 2 blokuje zasób 2 a potem zasób 1 Thread t2 = new Thread () { public void run () { synchronized ( resource2 ) { System.out. println ( " Thread 2: locked resource 2 " ); try { Thread. sleep (100); catch ( Exception e ) { synchronized ( resource1 ) { System.out. println ( " Thread 2: locked resource 1 " ); ; t1. start (); t2. start (); 32 / 36
Komunikacja mi dzyw tkowa Komunikacja mi dzyw tkowa sªu»y do kooperacji pomi dzy w tkami Gªównie u»ywa si jej do komunikacji, ze dany zasób jest dost pny do u»ycia, np. watek pisarza blokuje bufor, pisze do bufora a na koniec informuje w tek czytelnika,»e mo»na ju» odczyta dane metoda wait() - powoduje,»e watek znosi blokad i czeka dopóki inny watek nie wywoªa metody notify() lub notifyall() dla danego obiektu lub kiedy upªyn ª odpowiedni czas metoda notify() - powoduje obudzenie w tku czekaj cego na monitorze obiektu. Je±li czeka wiele w tków, to jeden z nich zostanie wybrany do obudzenia metoda notifyall() - budzi wszystkie w tki czekaj ce na monitorze obiektu 33 / 36
Proces komunikacji mi dzyw tkowej 34 / 36
Komunikacja mi dzyw tkowa - przykªad class Customer { int amount =10000; synchronized void withdraw ( int amount ){ System.out. println ( " going to withdraw... " ); if ( this. amount < amount ){ System.out. println ( "Less balance ; " + " waiting for deposit... " ); try {wait (); catch ( Exception e ){ this. amount -= amount ; System.out. println ( " withdraw completed... " ); synchronized void deposit ( int amount ){ System.out. println ( " going to deposit... " ); this. amount += amount ; System.out. println ( " deposit completed... " ); notify (); class Test { public static void main ( String args []){ final Customer c=new Customer (); new Thread (){ public void run (){ c. withdraw (15000);. start (); new Thread (){ public void run (){ c. deposit (10000);. start (); 35 / 36
36 / 36 Pytania?