UL/FRI/VSP-RI/RK/Vaje/2007-03-07

Iz E-študij, proste zakladnice študentskega znanja

< UL | FRI | VSP-RI | RK
Skoči na: navigacija, iskanje

Vaje z dne 07.03.2007
UL/FRI/VSP-RI/RK


Vodil: Matjaž Pančur

Življenjski cikel niti

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

  1. Deduj od razreda Thread (java.lang)
  2. Koda, ki se naj izvede se nahaja v
    public void run() {...}
  3. Kreiraj objekt (ali več objektov, po enega za vsako nit)
  4. 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:

  1. Daj kodo, ki jo želiš izvesti v niti v metodo run() v našem razredu
  2. Implementiraj vmesnik Runnable
  3. Naredi instanco razreda Thread, parameter je instanca razreda, ki implementira Runnable
    Thread t = new Thread(theRunnableObject);
  4. 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:

  1. Konča svojo run() metodo
  2. Nit sproži izjemo, ki je ne ulovimo
  3. (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

  1. A. Chaffee, Networking.ppt, purpletech.com
  2. D. Reilly, M. Reilly: Java Network Programming and Distributed Computing, AW, 2002 (na voljo v LRK)
  3. IBM-ov developerWorks Tutorial j-Threads, www.ibm.com/developerWorks
  4. M. Hall, W. Brown, Core Web Programming, 2ed; prosojnice iz knjige na notes.corewebprogramming.com
  5. 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

  1. Kreiraj objekt Socket
  2. Kreiraj izhodni tok (pisanje)
  3. Kreiraj vhodni tok (branje)
  4. Pošiljaj in sprejemaj podatke
  5. 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

  1. 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

  1. Kreiraj objekt ServerSocket
  2. Kreiraj objekt Socket iz ServerSocket
  3. Kreiraj vhodni tok
  4. Kreiraj izhodni tok
  5. Pošiljaj in sprejemaj podatke
  6. 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)
Osebna orodja
Imenski prostori
Različice
Dejanja
navigacija

Tiskanje/izvoz
orodja