21 novembre 2008 Séance 2 Les threads ====== Cours ====== Le but de ce cours est d'utiliser la puissance des nouveaux processeurs pour faire des applications rapides. threads = sous partie d'un processus, donc se lance plus vite qu'un processus et prend moins d'espace mémoire Faire un thread implique une nouvelle pile d'exécution et de nouveaux registres alloués. ===== Les Threads, dans Java ===== Dans la classe Java.lang.Thread, il faut l'interface *Runnable :*, et la méthode *run()* (qui ne prend pas d'arguments). Faire un thread en Java : * soit on étend (sous - classer) un thread (à l'aide d'extends) * soit on implémente *Runnable* à notre classe Java. ===== Comment choisir quelle méthode utiliser ? ===== Comme Java n'accepte pas l'héritage multiple, mieux vaut implémenter qu'étendre si on a déjà étendu notre classe Java avec une autre. Implémenter "marche à tout les coups", contrairement à l'autre méthode. ===== Gestion des priorités ===== Si un thread n'est pas actif, il ne consommera pas de temps CPU, vu qu'il est mis de côté. MAX_PRIORITY et MIN_PRIORITY sont des variables définies dans la classe Thread. ===== Concurrence d'accès ===== Pour éviter l'interblocage, on utilise le mot clé *synchronized* pour gérer les concurrences d'accès. ==== Concurrence d'accès sur une méthode ==== Le verrou est sur l'instance de la classe et pas sur la classe elle même Mettre un verrou pour une instance d'un champ statique (static) n'est pas la bonne solution !!! ==== Contrôler l'accès à un objet ==== Utile si la classe a été créée sans être prévue/pensée pour les Threads. Ceci permet d'utiliser une classe dans des Threads. ===== Groupes de Threads ===== Seulement si on a beaucoup de threads (en faisant des *ThreadGroup*). Permet de lancer un ensemble de threads sans avoir à les instancier l'un après l'autre. ====== Exercice ====== ===== Démonstration du cas de l'executor ===== 3 producteurs ont une liste de produits. Ils gèrent une file d'attente. Les consommateurs 1 et 2 tentent d'accéder à la file d'attente. Il faut gérer les processus inactifs et la synchronisation. * * * Executor ex = | Executors.new SingleThread(); | Executors.new ThreadPool(2); for (int i=0 ; i < 1 000 ; i++) ex.execute(new Runnable() { public void run() { System.out.println("o") } }); Dans les exécuteurs il n'y a pas de gestion de dépendances, donc on ne sait pas si le 5ième envoyé sera exécuté après le 4ième : ça faut le faire à la main. ===== Cas d'interblocage ===== Tout objet class DeadLock { private final Object lock1 = new Object(); private final Object lock2 = new Object(); void a() throws Interrupted Exception { lock1.wait(); lock2.notify(); } void b() throws Interrupted Exception { lock2.wait(); lock1.notify(); } } Un thread appelle la méthode a() : * Le lock1 passe en attente. Un second thread appelle la méthode b() : * Il se met en attente d'un notify (comme le lock1) Donc cela fait un verrou mortel, car on en sort pas. Pourquoi : * lock1 attend lock1.notify (qui est dans lock2) * lock2 attend lock2.notify (qui est dans lock1, lui même en attente d'un notify) Nous sommes donc dans un DeadLock. ===== Explications ===== wait() : suspend un thread courant notify(): réactive UN thread qui a fait un *wait()* sur l'instance notifyAll(): réactiver TOUS les threads qui ont fait un *wait()* sur l'instance Quand un producteur aura fini de produire, il va activer UN thread pour le consommateur : pour pas mettre sur la paille le second consommateur.