UL/FRI/VSP-RI/RK/Vaje/2007-03-07
Iz E-študij, proste zakladnice študentskega znanja
|
Vaje z dne 07.03.2007
Vodil: Matjaž Pančur |
Niti v Javi
Zakaj niti?
- Asinhrono procesiranje oz. procesiranje “v ozadju”
- Bolj odziven uporabniški vmesnik
- Boljše izkoriščanje večprocesorskih sistemov
- večjedrni procesorji
- ...
Zakaj ne niti?
- težje vzdrževanje
- težje razhroščevanje
Tvorba niti
V javi tvorimo niti na dva načina:
- Podrazred razreda Thread
- Implementacija vmesnika Runnable
Razred Thread
Nit ni objekt
- Nit je kontrolni tok
- Serija izvajanih ukazov
Instanca razreda Thread je objekt; ostane tudi po koncu izvajanja niti
Metodi razreda Thread:
- void start()
- kreira novo nit in jo zažene
- void run()
- na novo kreirana nit izvrši kodo v tej metodi
Uporaba v kodi
Uporabimo lahko 2 različna načina:
Podrazred Thread
- Deduj od razreda Thread (java.lang)
- Koda, ki se naj izvede se nahaja v
- public void run() {...}
- Kreiraj objekt (ali več objektov, po enega za vsako nit)
- Pokliči start()
Pozor, koda se nahaja v run(), vendar se nit zažene s start().
public class DriverClass extends SomeClass { ... public void startAThread() { // Create a Thread object. ThreadClass thread = new ThreadClass(); // Start it in a separate process. thread.start(); ... } }
public class ThreadClass extends Thread { public void run() { // Thread behavior here. } }
Primer uporabe
public class Counter extends Thread { private static int totalNum = 0; private int currentNum, loopLimit; public Counter(int loopLimit) { this.loopLimit = loopLimit; currentNum = totalNum++; } private void pause(double seconds) { try { Thread.sleep(Math.round(1000.0*seconds)); } catch(InterruptedException ie) {} } /** When run finishes, the thread exits. */ public void run() { for(int i=0; i<loopLimit; i++) { System.out.println("Counter " + currentNum + ": " + i); pause(Math.random()); // Sleep for up to 1 second } } }
public class CounterTest { public static void main(String[] args) { Counter c1 = new Counter(5); Counter c2 = new Counter(5); Counter c3 = new Counter(5); c1.start(); c2.start(); c3.start(); } }
Vmesnik Runnable
Ker java ne podpira večkratnega dedovanja imamo na voljo tudi alternativo z vmesnikom runnable:
- Daj kodo, ki jo želiš izvesti v niti v metodo run() v našem razredu
- Implementiraj vmesnik Runnable
- Naredi instanco razreda Thread, parameter je instanca razreda, ki implementira Runnable
- Thread t = new Thread(theRunnableObject);
- Pokliči metodo start()
- t.start();
public class ThreadClass extends AnyClass implements Runnable { public void run() { // Thread behavior here. } public void startThread() { Thread t = new Thread(this); t.start(); // Calls back to the run method in "this" } ... }
Primer uporabe
public class Counter2 implements Runnable { private static int totalNum = 0; private int currentNum, loopLimit; public Counter2(int loopLimit) { this.loopLimit = loopLimit; currentNum = totalNum++; } private void pause(double seconds) { try { Thread.sleep(Math.round(1000.0*seconds)); } catch(InterruptedException ie) {} } public void run() { for(int i=0; i<loopLimit; i++) { System.out.println("Counter " + currentNum + ": " + i); pause(Math.random()); // Sleep for up to 1 second. } public startThread(){ Thread t = new Thread(this); t.start(); }
public class Counter2Test { public static void main(String[] args) { Counter2 c1 = new Counter2(5); Counter2 c2 = new Counter2(5); Counter2 c3 = new Counter2(5); c1.startThread(); c2.startThread(); c3.startThread(); } }
Razred Thread - Končnje niti
Nit se konča na enega od 3 načinov:
- Konča svojo run() metodo
- Nit sproži izjemo, ki je ne ulovimo
- (ne uporabljaj ker ne počisti za seboj) - thread.stop()
Prioriteta niti (thread.setPriority())
- Privzeto je novi niti nastavljena enaka prioriteta kot je prioriteta niti, ki jo je ustvarila
- Thread.MAX_PRIORITY
- Thread.NORM_PRIORITY
- Thread.MIN_PRIORITY
- Večinoma se je bolje izogibati nastavljanju prioritete niti, ker se lahko zgodi, da se nit ne bo nikoli izvedla (prenizka prioriteta) ali pa bo imela tako visoko prioriteto, da operacijski sistem ne more več delovti normalno (zamrzne).
- Thread.currentThread()
- Vrne referenco na trenutno izvajano nit
- t.interrupt()
- Zbuditev niti
- Če nit t izvaja join(), sleep() ali wait(), se sproži InterruptedException
- V ostalih primerih se nastavi prekinitvena zastavica
- Thread.interrupted()
Preveri, ali ima trenutno izvajana nit nastavljeno zahtevo za prekinitev (nastavljeno zastavico) in jo (če je potrebno) zbriše
- t.isInterrupted()
- Preveri, ali je zastavica za prekinitev nastavljena (same zastavice ne spreminja!)
“Združevanje” niti:
- join()
- nit, ki pokliče t.join(), čaka (neskončno dolgo) da se nit t konča, nato nadaljuje.
- join(int ms)
- čaka največ ms milisekund, da se nit konča
Čakanje - Thread.sleep(long ms) (čaka ms milisek. ali pa na interrupt() – v zadnjem primeru se sproži izjema InterruptedException
- Pritajena nit
- t.isDaemon(), t.setDaemon()
- Program v Javi se konča, ko ostanejo aktivne le še pritajene niti
- Preveri, če je nit pritajena oz. nastavi nit za pritajeno
- Thread.yield()
- Nit se pomakne na konec vrste za izvajanje; s tem omogoči izvajanje drugim nitim
- Če imajo vse čakajoče niti manjšo prioriteto, se nit izvaja naprej.
- wait()
- Metoda razreda Object
- Sprosti zaklepanje in se preneha izvajati (gre v čakalno vrsto objekta)
- Nato čaka, dokler nekdo ne pokliče notify() ali notifyAll() (oz. je čakanje prekinjeno z interrupt())
- notify(), notifyAll()
- Metode razreda Object
- Zbudi nit oz. vse niti, ki čakajo v čakalni vrsti objekta
Ustavljanje niti
public class ThreadExample extends Thread { volatile private boolean running; public void run() { running = true; while (running) { //do some stuff } } public void pleaseStop() { running = false; } }
Sinhronizacija
synchronized (le ena nit v zaščiten del kode - mutex)
- Na nivoju bloka
- synchronized(Object o) { code...}
- Na nivoju metode
- public synchronized void myMethod() {code}
- Ekvivalentno:
- public void myMethod() {
- synchronized(this) { code...}
- }
- volatile
- Vsako pisanje gre direktno v pomnilnik (zaradi predpomnilnikov!), s tem vse niti vidijo isto vrednost
- Uporablja se le za kontroliran dostop do primitivnih spremenljivk, npr. pri raznih zastavicah (ne za števce!)
Sinhronizacija
Smernice za uporabo synchronized:
- Bloki naj bodo kratki
- Ne blokiraj znotraj synchronized bloka (v niti ne kliči metod, ki blokirajo izvajanje (npr. inputStream.read(), ...))
- Ne kliči metod v drugih objektih
- malo ekstremno (v praksi velikokrat ni možno), se pa s tem izognemo smrtnim objemom (deadlocks)
Primeri
Primeri opisani na http://www.javaworld.com/javaworld/jw-06-2002/jw-0607-java101.html
Brez sinhronizacije
// NeedForSynchronizationDemo.java class NeedForSynchronizationDemo { public static void main (String [] args) { FinTrans ft = new FinTrans (); TransThread tt1 = new TransThread (ft, "Deposit Thread"); TransThread tt2 = new TransThread (ft, "Withdrawal Thread"); tt1.start (); tt2.start (); } } class FinTrans { public static String transName; public static double amount; } class TransThread extends Thread { private FinTrans ft; TransThread (FinTrans ft, String name) { super (name); // Save thread's name this.ft = ft; // Save reference to financial transaction object } public void run () { for (int i = 0; i < 100; i++) { if (getName ().equals ("Deposit Thread")) { // Start of deposit thread's critical code section ft.transName = "Deposit"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 2000.0; System.out.println (ft.transName + " " + ft.amount); // End of deposit thread's critical code section } else { // Start of withdrawal thread's critical code section ft.transName = "Withdrawal"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 250.0; System.out.println (ft.transName + " " + ft.amount); // End of withdrawal thread's critical code section } } } }
Sinhronizacija 1
// SynchronizationDemo1.java class SynchronizationDemo1 { public static void main (String [] args) { FinTrans ft = new FinTrans (); TransThread tt1 = new TransThread (ft, "Deposit Thread"); TransThread tt2 = new TransThread (ft, "Withdrawal Thread"); tt1.start (); tt2.start (); } } class FinTrans { public static String transName; public static double amount; } class TransThread extends Thread { private FinTrans ft; TransThread (FinTrans ft, String name) { super (name); // Save thread's name this.ft = ft; // Save reference to financial transaction object } public void run () { for (int i = 0; i < 100; i++) { if (getName ().equals ("Deposit Thread")) { synchronized (ft) { ft.transName = "Deposit"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 2000.0; System.out.println (ft.transName + " " + ft.amount); } } else { synchronized (ft) { ft.transName = "Withdrawal"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 250.0; System.out.println (ft.transName + " " + ft.amount); } } } } }
Sinhronizacija 2
// SynchronizationDemo2.java class SynchronizationDemo2 { public static void main (String [] args) { FinTrans ft = new FinTrans (); TransThread tt1 = new TransThread (ft, "Deposit Thread"); TransThread tt2 = new TransThread (ft, "Withdrawal Thread"); tt1.start (); tt2.start (); } } class FinTrans { private String transName; private double amount; synchronized void update (String transName, double amount) { this.transName = transName; this.amount = amount; System.out.println (this.transName + " " + this.amount); } } class TransThread extends Thread { private FinTrans ft; TransThread (FinTrans ft, String name) { super (name); // Save thread's name this.ft = ft; // Save reference to financial transaction object } public void run () { for (int i = 0; i < 100; i++) if (getName ().equals ("Deposit Thread")) ft.update ("Deposit", 2000.0); else ft.update ("Withdrawal", 250.0); } }
Napačna sinhronizacija
// NoSynchronizationDemo.java class NoSynchronizationDemo { public static void main (String [] args) { FinTrans ft = new FinTrans (); TransThread tt1 = new TransThread (ft, "Deposit Thread"); TransThread tt2 = new TransThread (ft, "Withdrawal Thread"); tt1.start (); tt2.start (); } } class FinTrans { public static String transName; public static double amount; } class TransThread extends Thread { private FinTrans ft; TransThread (FinTrans ft, String name) { super (name); // Save thread's name this.ft = ft; // Save reference to financial transaction object } public void run () { for (int i = 0; i < 100; i++) { if (getName ().equals ("Deposit Thread")) { synchronized (this) { ft.transName = "Deposit"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 2000.0; System.out.println (ft.transName + " " + ft.amount); } } else { synchronized (this) { ft.transName = "Withdrawal"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 250.0; System.out.println (ft.transName + " " + ft.amount); } } } } }
Smrtni objem pri sinhronizaciji
// DeadlockDemo.java class DeadlockDemo { public static void main (String [] args) { FinTrans ft = new FinTrans (); TransThread tt1 = new TransThread (ft, "Deposit Thread"); TransThread tt2 = new TransThread (ft, "Withdrawal Thread"); tt1.start (); tt2.start (); } } class FinTrans { public static String transName; public static double amount; } class TransThread extends Thread { private FinTrans ft; private static String anotherSharedLock = ""; TransThread (FinTrans ft, String name) { super (name); // Save thread's name this.ft = ft; // Save reference to financial transaction object } public void run () { for (int i = 0; i < 100; i++) { if (getName ().equals ("Deposit Thread")) { synchronized (ft) { synchronized (anotherSharedLock) { ft.transName = "Deposit"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 2000.0; System.out.println (ft.transName + " " + ft.amount); } } } else { synchronized (anotherSharedLock) { synchronized (ft) { ft.transName = "Withdrawal"; try { Thread.sleep ((int) (Math.random () * 1000)); } catch (InterruptedException e) { } ft.amount = 250.0; System.out.println (ft.transName + " " + ft.amount); } } } } } }
Deljen števec
Pri deljenem števcu je naslednje stanje spremenljivke odvisno od prejšnjega stanja.
public class Counter { private int counter = 0; public int get() { return counter; } public void set(int n) { counter = n; } public void increment() { set(get() + 1); } }
Ne pomaga:
- volatile counter
- synchronized get() in synchronized set(int i)
- Npr. če gresta dve niti hkrati v increment(), kaj se lahko zgodi?
Potrebno je sinhronizirati increment()
- synchronized increment();
Viri
- A. Chaffee, Networking.ppt, purpletech.com
- D. Reilly, M. Reilly: Java Network Programming and Distributed Computing, AW, 2002 (na voljo v LRK)
- IBM-ov developerWorks Tutorial j-Threads, www.ibm.com/developerWorks
- M. Hall, W. Brown, Core Web Programming, 2ed; prosojnice iz knjige na notes.corewebprogramming.com
- Jeff Friesen, Achieve strong performance with threads, Part 2, Javaworld 6/2002, http://www.javaworld.com/javaworld/jw-06-2002/jw-0607-java101.html
Java V/I (I/O), vtiči (sockets)
Java IO, tokovi
- paket java.io
- Tokovi (streams)
- Da dobimo ustrezno povezavo (tok), lahko različne tokove vežemo med seboj
- Tok bajtov (byte oriented)
- Streams
- Tok znakov (character oriented)
- Readers, Writers
- Med katerokoli V/I operacijo se lahko sproži IOException
- Blokirajoči klici V/I (blocking I/O)
- Neblokirajoči klici (nonblocking I/O): od Jave1.4 naprej, java.nio
TCP, UDP v Javi
TCP (bolj pogost; razred Socket)
- Podatkovni tok (data stream)
- Zanesljiv (reliable)
- Urejen (ordered)
UDP (razred DatagramSocket)
- Datagrami (paketi)
- Nezanesljiv (unreliable)
- Neurejen (unordered)
Vtiči, vrata (sockets, ports)
- Vtič (socket)
- abstrakcija povezave med dvema računalnikoma; dvosmerna komunikacija
- Vrata (port)
- točka srečanja na strežniku
- Le ena storitev lahko teče na enih ena vratih
- 1-1023: znana vrata (standardne storitve)
- 1024 naprej – začasne storitve
Razred Socket
Paket java.net
- Socket(String host, int port)
- InputStream getInputStream()
- OutputStream getOutputStream()
- void close()
Odjemalec
- Kreiraj objekt Socket
- Kreiraj izhodni tok (pisanje)
- Kreiraj vhodni tok (branje)
- Pošiljaj in sprejemaj podatke
- Zapri Socket
//Kreiraj objekt Socket Socket client = new Socket("www.krneki.si", 80); //Kreiraj izhodni tok (pisanje) // parameter "true" - autoFlush za println PrintWriter out = new PrintWriter(client.getOutputStream(), true); //Kreiraj vhodni tok (branje) BufferedReader in = new BufferedReader( new InputStreamReader( client.getInputStream())); //Posiljaj in sprejemaj podatke //Za izhodni tok (PrintWriter) uporabi print, println //Za vhodni tok (BufferedReader) uporabi read in readLine //Zapri Socket client.close()
Zapre tudi vhodni in izhodni tok
Razred ServerSocket
- Paket java.net
- ServerSocket(int port)
- ServerSocket(int port, int numOfClients)
- numOfClients je privzeto 50
- Socket accept()
- Blokirajoči klic (Blocking I/O) - ko pokličemo program čaka na portu dokler se nekdo ne priklopi
- void setSoTimeout(int ms), int getSoTimeout()
- Čaka ms milisekund, nato se sproži SocketTimeoutException
- Če je ms=0, čaka neskončno dolgo (privzeto)
- void close()
Strežnik
- Kreiraj objekt ServerSocket
- Kreiraj objekt Socket iz ServerSocket
- Kreiraj vhodni tok
- Kreiraj izhodni tok
- Pošiljaj in sprejemaj podatke
- Zapri Socket
//Kreiraj objekt ServerSocket ServerSocket listenSocket = new ServerSocket(portNumber); //Kreiraj objekt Socket iz ServerSocket while (someCondition) { Socket server = listenSocket.accept(); doSomethingWith(server); } //doSomethingWith običajno sproži novo nit //Kreiraj vhodni tok BufferedReader in = new BufferedReader( new InputStreamReader( server.getInputStream())); //Kreiraj izhodni tok // parameter "true" - autoflush za println PrintWriter out = new PrintWriter(server.getOutputStream(), true); //Pošiljaj in sprejemaj podatke //Vhod: readLine //Izhod: println //Zapri Socket server.close()
Zapre tudi vhodni in izhodni tok
Izjeme pri vtičih (SocketException)
Vse so podrazredi IOException
- SocketException
- generična izjema
- BindException
- pri vezanju na vrata
- ConnectException
- vtič se ne more priklopiti na host, port
- NoRouteToHostException
- ne more najti poti do računalnika
- InterruptedIOException
- timeout pri branju
Primeri
http://archive.corewebprogramming.com/Chapter17.html
NetworkServer.java
SocketUtil.java
NetworkServerTest.java
EchoServer.java
Kaj pa če je več odjemalcev?
Niti (threads) (ThreadedEchoServer.java)
Non-blocking IO (Java 1.4, paket java.nio)
NetworkClient.java
SocketUtil.java
NetworkClientTest.java
Viri
- M. Hall, W. Brown, Core Web Programming, 2ed, notes.corewebprogramming.com
- A. Chaffee, Networking.ppt, purpletech.com
- D. Reilly, M Reilly: Java Network Programming and Distributed Computing, AW, 2002 (na voljo v LRK)
