Marco Ronchetti
Java
J0
1
Threads & Sincronizzazione
Threads - cenni di base
Una classe può essere threaded se:
Marco Ronchetti
è sottoclasse di Thread
oppure
implementa l’interfaccia Runnable.
In entrambi i casi DEVE implementare il metodo run
che viene eseguito quando la thread parte.
J0
2
Threads - cenni di base
Marco Ronchetti
Public class C extends Thread {
public void run() { //implementazione di run }
}
J0
3
Nel codice di una Classe che usa C:
…
C athread = new C();
athread.start(); // attiva il metodo run
boolean f=athread.isAlive(); after start before end
Threads - cenni di base
Marco Ronchetti
Public class C implements Runnable {
public void run() { //implementazione di run }
}
Nel codice di una Classe che usa C:
…
C aRunnableClass = new C();
Thread athread = new Thread(aRunnableClass);
athread.start(); // attiva il metodo run
boolean f=athread.isAlive(); after start before end
J0
4
Marco Ronchetti
Metodi principali di Thread
J0
5
Thread athread
athread.start()
// attiva il metodo run
athread.join(anotherThread) //attende la fine di anotherThread
…
Thread.yield();
//cede la CPU (sospensione non temporizzata)
Thread.sleep(long milliseconds); // sospensione temporizzata
Thread.sleep(long milliseconds, int nanoseconds);
athread.setDaemon(); athread.isDaemon() //analogo ai demoni unix
METODI DEPRECATI!
athread.stop();
// interrompe il metodo run (termina)
athread.suspend();
// sospende indefinitamente il metodo run
athread.resume();
// riprende dopo una suspend
athread.destroy();
// distrugge la Thread
Daemon Threads
Marco Ronchetti
Ci sono due tipi di threads: user threads e daemon
threads.
La differenza sta nel fatto che l’applicazione/applet
termina quando non ci sono piu’ user threads.
Le daemon threads servono dunque per effettuare
servizi a favore delle user threads. Tutte le threads di
sistema sono daemon threads.
void setDaemon(boolean on)
boolean isDaemon ()
J0
6
import java.awt.Graphics;
import java.util.Date;
Applet methods - 3
public class ShowAppletMethods extends java.applet.Applet
Date d;
static int count=0;
simpleThread t;
{
class simpleThread extends Thread {
ShowAppletMethods a;
int seconds;
simpleThread(ShowAppletMethods a, int s)
{
this.a=a;
seconds=s*1000;
}
Marco Ronchetti
public void init() {
System.out.println((count++)+" Init Executed");
int seconds=Integer.parseInt(this.getParameter(
"Secondi"));
t=new simpleThread(this,seconds);
t.start();
}
public void start() {
public void run() {
System.out.println((count++)+" Start Executed");
while (true) {
}
try {
public void stop() {
sleep(seconds);
System.out.println((count++)+" Stop Executed");
}
}
catch (InterruptedException e) {}
public void destroy() {
System.out.println("==> THREAD:
System.out.println((count++)+" Destroy Executed"); calling repaint");
}
a.repaint();
public void paint(Graphics g) {
}
System.out.println((count++)+" Paint Executed");
}
d=new Date();
}
g.drawString("Time now is "+d.getHours() +":" +d.getMinutes() +":"
+d.getSeconds(), 5, 25);
J0
}
7}
Marco Ronchetti
Perche’ Threads ?
Nonblocking I/O
tecniche standard non disponibili in Java:
I/O multiplexing
Polling
Signals
Alarms & Timers
Support for (logically) parallel operations
J0
8
Controllo delle Threads: rendez-vous
Marco Ronchetti
Class T extends Thread;
T t1 = new T();
T t2 = new T();
t2.join(); // nel codice di t1
sospende l’esecuzione di t1 fino al completamento di t2
ovvero fino a quando t2 sara’ considerata “non attiva”
void join();
void join(long m); //sospendi, ma per non piu’ di m milliseconds
void join(long m, int n); //sospendi, ma per non piu’ di
// m milliseconds e n nanoseconds
J0
9
Behind the scenes
Group
Description
System
Clock handler
gestisce sleep e wait(timeout)
Idle thread
attiva il garbage collector
Garbage Collector libera gli oggetti non usati
Finalizer Thread gestisce il metodo finalize() sugli
oggetti liberati
Main
Main
Marco Ronchetti
J0
10
Name
esegue il metodo main()
dell’applicazione
Behind the scenes - Applets
Group
Marco Ronchetti
System
Name
---- (come per le applicazioni) -----
AppletViewer
AWT-Input
AWT-Toolkit
Screen updater
Image fetcher
Audio player
AppletName
AppletName
J0
11
Description
gestisce l’input dal windowing
system nativo
tratta gli eventi del win. sys. nativo
gestisce la coda di repaint ed esegue
paint
carica immagini della rete (4 threads)
gestisce l’output sonoro
Applet Thread (esegue start(0, stop() ecc.)
Marco Ronchetti
Behind the scenes - Applets
J0
12
In una applet che gestisca immagini e
suono, ci sono almeno 12 threads attive
oltre a quella propria della Applet!
Marco Ronchetti
Sezione critica
t1
int k=c.incr();
int n = count;
count= n + 1;
return n;
t2
int k=c.incr();
int n = count;
count= n + 1;
return n;
int k=c.incr();
int n = count;
int k=c.incr();
int n = count;
count= n + 1;
return n;
count= n + 1;
return n;
J0
13
package counter;
public class CounterUser {
import java.io.*;
public static void main(String arg[]) {
class Counter {
Counter cont=new Counter();
private int count = 0;
T t1 = new T(cont);
int maxiter=233;
T t2 = new T(cont);
public int incr() {
t1.start();A
int n = count;
t2.start();
delay(maxiter);
count
}
count=
n
+
1;
0
}
return n;
0
}
1
public void delay(int maxiter) {
1
double z=1;
1
for (int k=0;k<maxiter;k++) {
z = Math.sqrt(Math.sin(Math.random()*z));
1
}
2
class T extends Thread {
}
2
Counter c;
}
2
T(Counter c) {
this.c=c;
2
}
2
public void run() {
2
int k=0;
3
while (true) {
3
k=c.incr();
3
System.out.println(this.toString()+" - "+k);
c.delay(20*c.maxiter);
3
}
}
}
Sezione critica
Marco Ronchetti
t1
int k=c.incr();
int n = count;
count= n + 1;
return n;
t2
count
int k=c.incr();
int n = count;
count= n + 1;
return n;
int k=c.incr();
int n = count;
0
0
1
1
1
1
2
2
2
2
int k=c.incr();
int n = count;
count= n + 1;
return n;
count= n + 1;
return n;
J0
14
3
3
package counter;
import java.io.*;
class Counter {
private int count = 0;
int maxiter=233;
public syncronized int incr() {
int n = count;
delay(maxiter);
count= n + 1;
return n;
}
public void delay(int maxiter) {
double z=1;
for (int k=0;k<maxiter;k++) {
z = Math.sqrt(Math.sin(Math.random()*z));
}
}
}
Marco Ronchetti
Concorrenza: metodi sincronizzati
Una sola thread alla volta puo’ eseguire
un metodo synchronized su un dato oggetto
(su oggetti diversi sono possibili esecuzioni
“parallele” di metodi synchronized!)
Concetto di MONITOR
(Analogia: bastone della staffetta)
OGNI OGGETTO POSSIEDE UN
MONITOR!
J0
15
(ma anche le Classi possono possedere un
monitor, per i metodi “static”)
class A {
public void doSomething(Object o) {
…
synchronized(o) {
sezione critica di A
}
...
}
class B {
public void doSomethingElse(Object k) {
…
synchronized(k) {
sezione critica di B
}
...
}
…
Point o1 = new Point();
A a1=new A();
A a2=new A();
B b1=new B();
B b2=new B();
Concorrenza: blocchi sincronizzati
Marco Ronchetti
A granularita’ piu’ bassa, e’ possibile
sincronizzare blocchi di codice su di un
monitor.
J0
16
Nell’esempio a fianco, le sezioni critica
di a1,a2,b2 NON possono essere
eseguite parallelamente (sono
sincronizzate tramite il monitor
dell’oggetto o1.
La sezione critica di b1 invece puo’
essere eseguita in parallelo a quelle di
a1,a2,b2 perche’ si sincronizza con un
diverso monitor.
da thread diverse, vengono invocati:
a1.doSomething(o1);
b1.doSomethingElse(this);
a2.doSomething(o1);
b2.doSomethingElse(o1);
Marco Ronchetti
Chiamate sincronizzate ricorsive
Class A {
synchronized void m1() {
…
m2()
…
}
synchronized void m2() {
…
}
...
Questo codice NON da’ origine a deadlock! La thread PUO’ eseguire m2
perche’ GIA’ POSSIEDE il monitor relativo. (Un contatore tiene traccia
di quanti accessi sincronizzati allo stesso monitor vengono fatti).
J0
17
Marco Ronchetti
Soluzioni per il problema della concorrenza
Soluzioni classiche:
Monitors + condition variables (Hoare-Hansen
74/75)
Monitor: una collezione di procedure, variabili e strutture
dati raccolte in uno speciale modulo, con la proprietà che
un solo processo alla volta può eccedere ad un monitor.
C.V.: Variabili con due primitive: wait e notify.
wait mette il processo in attesa sulla c.v.
notify risveglia un processo in attesa sulla c.v.
J0
18
Marco Ronchetti
Concorrenza: wait & notify
synchronized (pippo) {
while (! condizione) {
pippo.wait();
}
}
synchronized (pippo) {
pippo.notify(); // risveglia il primo in coda su pippo
}
wait(long msec)
wait(long msec, int nsec)
J0
19
...
esegue una wait con timeout
esegue una wait con timeout
notifyAll() risveglia tutte le threads in attesa sullo stesso monitor
(usato ad esempio in MediaTracker)
public class PingPong {
//Main method
class Ping extends Thread {
PingPong() {
Object monitor1,monitor2;
Object z1=new Object();
Ping(Object o1,Object o2) {
Object z2=new Object();
this.monitor1=o1; this.monitor2=o2;
Ping t1=new Ping(z1,z2);
}
Pong t2=new Pong(z1,z2);
public void run() {
t1.start(); t2.start();
while (true) {
try
{
try {
class Pong extends Thread {
Thread.sleep(60*1000);
Thread.sleep(1000);
Object monitor1,monitor2;
} catch (InterruptedException i) {}
} catch (InterruptedException i) {}
Pong(Object o1,Object o2) {
t1.stop(); t2.stop();
System.out.println(">> Ping");
this.monitor1=o1;
}
synchronized(monitor1) {
this.monitor2=o2;
public
static
void
main
System.out.println(">> == Ping is notifying");
}
(String[]
args)
{
monitor1.notify();
public void run() {
new PingPong ();
}
while (true) {
}
synchronized(monitor2) {
synchronized(monitor1) {
}
try{
try{
System.out.println(">> == Ping is waiting");
System.out.println("<< == Pong is waiting");
monitor2.wait();
monitor1.wait();
} catch (InterruptedException e) {}
} catch (InterruptedException e) {}
}
}
}
System.out.println("<< Pong");
}
synchronized(monitor2) {
}
System.out.println("<< == Pong is notifying");
monitor2.notify();
}
J0 }
20 }
}
Marco Ronchetti
wait & notify
wait & notify
Marco Ronchetti
Attenzione alla perdita di messaggi!
J0
21
Cosa avviene se
la notify()
viene inviata
prima della
corrispondente
wait() ?
Concorrenza: wait & notify
Marco Ronchetti
NOTE su wait e notify:
wait() rilascia temporaneamente il monitor
J0
22
Non c’e’ modo di distinguere
se da una wait si e’
...
usciti per timeout o per notification
esecuzioni di wait o notify senza acquisizione del
monitor danno origine a
IllegalMonitorStateException
Produttore e consumatore con buffer
Marco Ronchetti
Problema classico:
J0
23
Due processi (Produttore e Consumatore) collaborano,
scambiandosi dati tramite un buffer condiviso.
Occorre evitare che :
- il Produttore tenti di scrivere se il buffer e’ pieno,
- il Consumatore tenti di leggere se il buffer e’ vuoto.
Produttore e Consumatore
package ProCon;
public static void main(String[] args) {
try {
ProCon p = new ProCon();
} catch (Exception w) {}
}
Marco Ronchetti
public class ProCon {
J0
24
ProCon() throws InterruptedException {
Buffer b = new Buffer(10);
Consumer c = new Consumer(b,600);
Producer p = new Producer(b,300);
Thread tc = new Thread(c);
Thread pc = new Thread(p);
tc.start();
pc.start();
pc.join();
tc.join();
System.out.println("ProCon resuming execution...");
synchronized (this) {
wait(3000);
}
}
}
Marco Ronchetti
Produttore e Consumatore
J0
25
package ProCon;
public class Buffer {
private char[] buf; // buffer storage
private int last; // last occupied position
public Buffer(int sz) {
buf = new char[sz];
last = 0;
}
public boolean isFull() { return (last == buf.length); }
public boolean isEmpty() { return (last == 0);
}
public synchronized void put(char c) {
while(isFull()) {
// wait for room to put stuff
try { wait(); } catch(InterruptedException e) { }
}
public synchronized char get() {
buf[last++] = c;
while(isEmpty()) {
// wait for stuff to read
notify();
try { wait(); } catch(InterruptedException e) { }
}
}
char c = buf[0];
System.arraycopy(buf, 1, buf, 0, --last);
notify();
return c;
}
}
Produttore e Consumatore
Marco Ronchetti
package ProCon;
public class Producer implements Runnable {
private Buffer buffer;
private int delay=0;
J0
26 }
package ProCon;
public class Consumer implements Runnable {
private Buffer buffer;
private int delay=0;
public Producer(Buffer b, int d) {
buffer = b;
delay = d;
}
public void run() {
for (int i=0; i<52; i++) {
char c = (char)('A' + (i%26));
System.out.println("Producing "+c);
buffer.put(c); // write to the buffer
try {
Thread.sleep(delay);
} catch (InterruptedException ie){}
}
System.out.println("Producer Ended");
}
public Consumer(Buffer b, int d) {
buffer = b;
delay = d;
}
public void run() {
for (int i=0; i<52; i++) {
System.out.println("Consuming "+buffer.get());
try {
Thread.sleep(delay);
} catch (InterruptedException ie){}
}
System.out.println("Consumer Ended");
}
}
Semafori con i monitor
Marco Ronchetti
public class Semaphore {
private int S=0;
private int waitingProcesses=0;
J0
27
Semaphore(int v) {
S=v;
}
public synchronized void down() {
if (S>0) S--;
else {
try {
waitingProcesses++;
wait();
} catch (InterruptedException e) {}
}
public synchronized void up() {
}
if (waitingProcesses == 0) S++;
else {
waitingProcesses --;
notify();
}
}
}
Produttore e Consumatore coi semafori
Marco Ronchetti
public class Buffer {
private char[] buf; // buffer storage
public class ProCon {
Le classi
private int last; // last occupied position
Semaphore empty;
producer e
ProCon() throws InterruptedException {
Semaphore full;
consumer
Semaphore mutex;
Buffer b = new Buffer(10);
non cambiano
Consumer c = new Consumer(b,600);
public Buffer(int sz) {
Producer p = new Producer(b,300);
public void put(char c) {
buf = new char[sz];
Thread tc = new Thread(c);
empty.down();
last = 0;
Thread pc = new Thread(p);
mutex.down();
empty=new Semaphore(sz);
tc.start();
buf[last++] = c;
full=new Semaphore(0);
pc.start();
mutex.up();
mutex=new Semaphore(1);
pc.join();
full.up();
}
tc.join();
}
System.out.println("ProCon resuming execution...");
synchronized (this) {
public char get() {
wait(3000);
full.down();
}
mutex.down();
}
char c = buf[0];
public static void main(String[] args) {
System.arraycopy(buf, 1, buf, 0, --last);
try {
mutex.up();
ProCon p = new ProCon();
empty.up();
} catch (Exception w) {}
return c;
J0 }
}
28 }
}
Marco Ronchetti
Threads scheduling
Lo scheduling della JVM e’ “inaffidabile”
(perché ancora indefinito), nel senso che
cambiando piattaforma cambia
implementazione.
Se serve uno scheduling con proprietà ben
definite e’ bene scrivere il proprio scheduler.
Per dettagli, vedere
Oaks and Wong, “Java Threads”, O’Reilly 1997
J0
29
A simple Round Robin scheduler
public class SimpleScheduler extends Thread {
int timeslice;
Marco Ronchetti
public SimpleScheduler(int t) {
timeslice=t;
setPriority(Thread.MAX_PRIORITY);
setDaemon(true);
}
public void run() {
while (true)
try {
sleep(timeslice);
} catch (Exception e) {}
}
}
J0
30
class TestThread extends Thread {
public void run() {
...
}
}
public class Test {
public static void main (string args[]) {
new SimpleScheduler(100).start();
TestThread t1,t2,t3;
t1=new TestThread();
t2=new TestThread();
t3=new TestThread();
t1.start();
t2.start();
t3.start();
}
}
Marco Ronchetti
Concorrenza: wait & notify
Si rimanda a
“Java Threads”
Di Oaks e Wong
Ed. O’Reilly
“Java Restaurant”
Ed. Apogeo
J0
31