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); } }