Multithreading i sincronització Edgar Ros Ferrer Xavier Farré Barbera Threads en Java Multithreading i sincronització Edgar Ros Ferrer Xavier Farré Barbera
Threads en Java Introducció als threads en Java: Sincronització: Implementació de processos (referent al SO). Declaració de threads en Java. Estats d’un thread en Java. Sincronització: Modificador synchronized. Monitors. Semàfors.
Introducció als threads en Java Definició de thread segons SUN: Thread: The basic unit of program execution. A process can have several threads running concurrently, each performing a different job, such as waiting for events or performing a time-consuming job that the program doesn’t need to complete before going on. When a thread has finished its job, the thread is suspended or destroyed. Resulta interesant comparar thread amb procès: Process: A virtual address space containing one or more threads.
Implementació de processos (referent al SO) Un procés (heavyweight process) està representat pel seu codi, dades i l’estat dels seus registres (context). Per suportar múltiples (lightwey) threads disposa d’una pila per cadascún d’ells.
Declaració de threads en Java Com extensió de la classe Thread. El codi executat pel thread és el mètode run() Thread run() MyThread class MyThread extends Thread { public void run() { //...... } Thread x = new MyThread();
Declaració de threads en Java Degut a que Java no permet herència múltiple, podem crear threads com a classe que implementa la interfície Runnable. Runnable run() MyRun public interface { public abstract void run(); } class implements public void run() { // ..... Thread target
Estats d’un thread en Java La crida start() provoca l’execució del mètode run() definit. Un thread pot acabar per dos motius: retorn del mètode run() o bé per mitjà del mètode stop() start() Created Alive stop(), or run() returns stop() Terminated
Subestats d’un thread iniciat Un cop iniciat el seu mètode run() els estats possibles són: start() Running sleep() suspend() yield() dispatch suspend() Runnable Non-Runnable resume() stop(), or run() returns
La cua de threads en espera Java proporciona una cua de threads en espera per cada objecte. Els mètodes relacionats són: public final void notify() Desperta un, i només un, thread que espera a la cua de l’objecte. public final void notifyAll() Desperta tots els threads que esperen a la cua de l’objecte. public final void wait() throws InterruptedException Adorm el thread, que continuarà a la cua d’espera fins que sigui despertat per un altre thread. Aquesta espera NO és activa.
Alguns métodes més sobre threads public final void suspend() Suspén l’execució del thread que crida aquest mètode. public final void resume() Reanuda l’execució d’un thread que hagi fet abans un suspend(). public final void stop() Mata el thread que crida aquest mètode. public final void join() Espera l’acabament d’un thread. public final static void yield() Permet que un altre thread que estigui preparat per seguir-se executant passi a executar-se, és a dir, es proposa una planificació.
Daemons Java permet diferenciar entre threads d’usuari i deamons. Una aplicació Java finalitza si acaba l’execució del main (i de tots els threads d’usuari) o bé si hi ha un o més threads deamon en execució (seguiràn corrent encara que acabi l’aplicació). Interfície dels threads deamon: public final void setDaemon(boolean on) Permet declarar un thread com a deamon. public final boolean isDaemon() Permet saber si un thread és o no un daemon.
Scheduling Les regles de planificació són complexes, però fàcils de posar en pràctica seguint unes regles bàsiques: Si un o més threads modifiquen un objecte, declararem els mètodes que realitzin les modificacions com a synchronized. Si un thread ha d’esperar un canvi en l’estat d’un objecte hauria d’esperar dintre de l’objecte, és a dir, executant un mètode synchronized i una crida wait(). Sempre que un mètode canviï l’estat d’un objecte s’ha de cridar al mètode notify() per despertar threads que podrien estar esperant aquest canvi en l’estat.
Sincronització: modificador synchronized Mecanisme proporcionat per Java. Com sincronitzem? Si una classe té un o més mètodes synchronized, cada objecte de la classe tindrà associada una cua d’espera. Aixó permetrà que diferents threads esperin per executar el mètode en exclusió mútua. Un thread pot entrar a la cua d’espera d’un mètode synchronized cridant-lo mentre un altre thread l’estigui executant o bé fent una crida wait() mentre està executant aquest mètode.
Sincronització: modificador synchronized Quan finalitza l’execució d’un mètode synchronized o bé quan es fa un wait() dintre del seu codi, un altre thread tindrà accés al mètode. El planificador tria el thread amb prioritat més alta d’entre els que esperen en una cua. D’entre els threads que tenen la mateixa prioritat no es pot garantir que l’ordre serà FIFO. Un thread que hagi estat insertat en una cua d’espera per mità d’una crida wait() haurà de ser despertat per una crida notify() o bé notifyAll() per que el planificador pugui posar-lo en execució.
Sincronització: monitors Diem que un thread “entra” en un monitor quan adquireix l’accés en exclusió mútua a l’objecte associat al monitor. Diem que un thread “surt” d’un monitor quan allibera el lock de l’objecte associat al monitor. Thread A Thread B wait() notify() Monitor data
Sincronització: monitors Esquema de mètode d’una classe monitor: public synchronized void funcio() throws InterruptedException { while (!condicio) wait(); // regió crítica notifyAll() } El bucle while és necessari per verificar que la condició es satisfà un cop el thread ha estat despertat. La crida notifyAll() desperta tots els threads que estiguin en espera per accedir a la regió crítica.
Sincronització: semàfors Mecanisme tradicional de sincronització i control d’accés a regions crítiques. Semàfors = comptador + cua En Java no cal implementar la cua del semàfor ja que tenim la cua d’espera associada als mètodes synchronized. A la pràctica, la implementeació d’un semàfor en Java acaba semblant-se a un monitor.
Sincronització: semàfors public class Semaphore { private int value; public Semaphore (int initial) {value = initial;} synchronized public void up() { ++value; notify(); } synchronized public void down() throws InterruptedException { while (value== 0) wait(); --value;