Join, interrupt i volatile
Join
Un join fa que un fil s'esperi a la finalització d'un altre. El fil que fa el join passa a l'estat WAITING fins que el fil que s'espera acaba. Si el fil que s'espera ja ha acabat, el join no fa res.
public class JoinThread {
static class MyRunnable implements Runnable {
@Override
public void run() {
log("starting");
spend(1500);
log("ending");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable(), "child");
log("starting");
thread.start();
log("started");
thread.join();
log("joined");
}
}
Interrupt
Una interrupció és una indicació a un fil de què ha de parar de fer el que està fent i fer una altra cosa. Els fils tenen un flag que indica si han estat interromputs o no. Hi ha dues formes de comprovar si un fil ha estat interromput:
- Que el fil faci crides freqüents a mètodes que facin throw de InterruptedException. Per exemple, Thread.sleep(). També serveix si la interrupció s'ha produït abans del sleep().
- Que el fil comprovi freqüentment Thread.currentThread().isInterrupted().
Un cop feta la comprovació i detectada la interrupció, el flag d'interrupció del fil es retorna a false
i cal decidir què fer. Normalment, el que farà és acabar la seva execució, però no és obligatori. Si no es fa res, el fil continuarà executant-se.
public class Interrupt1Thread {
static class MyRunnable implements Runnable {
@Override
public void run() {
log("starting");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
log("interrupted!");
}
log("ending");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable(), "child");
log("starting");
thread.start();
log("started");
thread.interrupt();
thread.join();
log("joined");
}
}
public class Interrupt2Thread {
static class MyRunnable implements Runnable {
@Override
public void run() {
log("starting");
while (!Thread.currentThread().isInterrupted());
log("ending");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new MyRunnable(), "child");
log("starting");
thread.start();
log("started");
rest(1500);
thread.interrupt();
thread.join();
log("joined");
}
}
Estat compartit
Es pot acabar un fil compartint una variable que un fil modifica i l'altre llegeix. En Java, cal definir la variable com a volatile
, indicant que els canvis fets en un fil siguin visibles en la resta. O bé utilitzar objectes segurs creats per nosaltres (mecanismes de sincronització) o d'una llibreria segura (p. ex. la atomic de Java).
La paraula clau volatile
només s'ha d'utilitzar si un fil escriu i l'altre (o altres) llegeixen. Si diversos fils escriuen i llegeixen, cal gestionar-ho com a zones crítiques.
public class VolatileThread {
static class SharedObject {
boolean done; // volatile keyword needed
}
static class MyRunnable1 implements Runnable {
SharedObject so;
MyRunnable1(SharedObject so) {
this.so = so;
}
@Override
public void run() {
spend(1500);
so.done = true;
}
}
static class MyRunnable2 implements Runnable {
SharedObject so;
MyRunnable2(SharedObject so) {
this.so = so;
}
@Override
public void run() {
boolean done = false;
while (!done) {
done = so.done;
}
}
}
public static void main(String[] args) throws InterruptedException {
SharedObject so = new SharedObject();
Thread t1 = new Thread(new MyRunnable1(so));
Thread t2 = new Thread(new MyRunnable2(so));
t1.start();
t2.start();
t1.join();
log("joined 1 with " + so.done);
t2.join();
log("joined 2 with " + so.done);
}
}