1 ift1025 – programmation 2 multi-thread jian-yun nie
TRANSCRIPT
1
IFT1025 – Programmation 2
Multi-Thread
Jian-Yun Nie
2
Concepts
• Thread:– Unité de programme qui est exécuté en
parallèle avec le reste des programmes
• Processus en parallèle– Plusieurs processus sont exécutés en même
temps
• Créer, lancer et terminer un thread• Interface Runnable• Synchronisation des threads
3
Interface Runnable
• Une méthode run() exigée:public interface Runnable{ void run();}
• Implantation:public class MyRunnable implements Runnable{ public void run() { // Task statements go here . . . }}
4
Créer un threadUn Thread est une classe qui implante Runnable
1. Créer une sous-classe de Thread (doit aussi implanter run() )
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
Utilisation: PrimeThread p = new PrimeThread(143); p.start();
5
Créer un thread
2. Un thread est créé à base d’un objet Runnable
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
6
Classe Thread
public class Thread extends Object implements Runnable
Constructeurs:Thread() Thread(Runnable target) Thread(String name) …
7
Classe Thread
• Méthodes:static int activeCount(): nb. de threads de ce groupevoid destroy()void interrupt()static void sleep(long millis) void start()static void yield(): faire une pause et permettre aux
autres threads d’exécuter (traitement de deadlock)…
8
Explication de thread
Séquence d’actions
• Bien ordonnée en parallèle
9
Exemple1: Définition de Thread• Créer une sous-classe de Thread:
public class SimpleThread extends Thread { public SimpleThread(String str) { super(str); }
public void run() { for (int i = 0; i < 10; i++) { System.out.format("%d %s%n", i, getName()); try { sleep((long)(Math.random() * 1000)); } catch (InterruptedException e) {} } System.out.format("DONE! %s%n", getName()); }}
Thread avec un nom
Méthode run():
10 fois: Afficher le nom du Thread et sleep un temps aléatoire
10
Exemple1: Utilisation du Thread
• Lancer 2 Threadspublic class TwoThreadsTest {
public static void main (String[] args) {
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
}
}
11
Différence avec un appel à run()
public class TwoThreadsTest { public static void main (String[] args) { new SimpleThread("Jamaica").run(); new SimpleThread("Fiji").run(); }}
0 Jamaica1 Jamaica2 Jamaica3 Jamaica4 Jamaica5 Jamaica6 Jamaica7 Jamaica8 Jamaica9 JamaicaDONE! Jamaica0 Fiji1 Fiji2 Fiji3 Fiji4 Fiji5 Fiji6 Fiji7 Fiji8 Fiji9 FijiDONE! Fiji
12
Exemple 2: Clockimport java.awt.*;import java.util.*;import java.applet.*;import java.text.*;
public class Clock extends java.applet.Applet implements Runnable { private volatile Thread clockThread = null; DateFormat formatter; //Formats the date displayed String lastdate; //String to hold date displayed Date currentDate; //Used to get date to display Color numberColor; //Color of numbers Font clockFaceFont; Locale locale;
public void init() { setBackground(Color.white); numberColor = Color.red; locale = Locale.getDefault(); formatter = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, locale); currentDate = new Date(); lastdate = formatter.format(currentDate); clockFaceFont = new Font("Sans-Serif", Font.PLAIN, 14); resize(275, 25); }
public void start() { if (clockThread == null) {
clockThread = new Thread(this, "Clock"); clockThread.start(); } }
public void run() { Thread myThread = Thread.currentThread(); while (clockThread == myThread) { repaint(); try { Thread.sleep(1000); } catch (InterruptedException e){ } } }
public void paint(Graphics g) { String today; currentDate = new Date(); formatter = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.MEDIUM, locale); today = formatter.format(currentDate); g.setFont(clockFaceFont);
//Erase and redraw g.setColor(getBackground()); g.drawString(lastdate, 0, 12);
g.setColor(numberColor); g.drawString(today, 0, 12); lastdate = today; currentDate = null; }
public void stop() { clockThread = null; }}
start de Applet
start de Thread – run()
stop: si la fenêtre ferme
Temps courant
Tant que non-fini, reffiche toute les secondes
13
Déclarer Thread avec Thread ou Runnable?
1. Créer une sous-classe de Thread2. Créer un Thread avec Runnable
• Les 2 méthodes sont possibles• Utiliser la 2-ième méthode si le Thread
doit être une sous-classe d’une autre classe (e.g. Clock)– La première méthode ne le permet pas– Une classe ne peut pas avoir 2 super-classes
14
États d’un Thead
• Création et lancement:public void start() { if (clockThread == null) { clockThread = new Thread(this, "Clock"); clockThread.start(); }}
• Entrer dans Runnable• Not Runnable: sleep(), E/S• Dead: terminaison: Fermer Applet -> exécuter stop()
public void stop() { //applet's stop method clockThread = null;}
15
Terminer un Thread
Schéma typique:
public void run(){ try { for (int i = 1; i <= REPETITIONS; i++) { // Do work } } catch (InterruptedException exception) { // Clean up }}
16
Exemplepublic class MyRunnable implements Runnable{ public void run() { try { System.out.println(1); Thread.sleep(1000); System.out.println(2); } catch (InterruptedException exception) { System.out.println(3); } System.out.println(4); }}• Sortie?
1, 3, 4
17
Détecter l’état d’un Thread
• Thread.getState():– NEW – RUNNABLE – BLOCKED – WAITING – TIMED_WAITING – TERMINATED
18
Synchronisation
Accès conflictuels:écrire lire
(put - producer) (get -consumer)
+2 -100X
-20*1.02
19
Exemple: Accéder au même BankAcountpublic class DepositRunnable
implements Runnable{public void run(){ try { for (int i = 1; i <= count; i++) { account.deposit(amount); Thread.sleep(DELAY); } } catch (InterruptedException
exception) { }}…
public class WithdrawRunnable implements Runnable
{ public void run(){
try {
for (int i = 1; i <= count; i++) {
account.withdraw(amount);Thread.sleep(DELAY);
}}catch (InterruptedException exception) {}
}…
20
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is 0.0
Depositing 100.0, new balance is 100.0
Withdrawing 100.0, new balance is 0.0
. . .
Withdrawing 100.0, new balance is 400.0
Depositing 100.0, new balance is 500.0
Withdrawing 100.0, new balance is 400.0
Withdrawing 100.0, new balance is 300.0
Problème
Depositing 100.0 Withdrawing 100.0, new balance is 100.0,
new balance is -100.0
new DepositRunnable().start();
new WithdrawRunnable().start();
21
Synchronisation: Solution 1• Bloquer la ressource durant un traitement:
public class BankAccount{ public BankAccount() { balanceChangeLock = new ReentrantLock(); . . . } . . . private Lock balanceChangeLock;}
• Utilisation typique:– balanceChangeLock.lock();– Code that manipulates the shared resource– balanceChangeLock.unlock();
22
Utilisation typiquepublic void deposit(double amount){ balanceChangeLock.lock(); try { System.out.print("Depositing " + amount); double newBalance = balance + amount; System.out.println(", new balance is " + newBalance); balance = newBalance; } finally { balanceChangeLock.unlock(); }}
Toujours dans BankAccount
Finally: Pour que ça fonctionne même si une exception est lancée
23
Interface Lock
• void lock(): bloquer la ressource• Condition newCondition(): associer une
condition (plus tard)• void unlock(): débloquer
Les classes qui implante l’interface Lock:ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
24
Schéma général
• Bloquer• Exécuter un bloc (try)• Débloquer (finally)
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
25
ReentrantLock• Une classe qui implante Lock• Plus de méthodes que celles exigées dans Lock
class X { private final ReentrantLock lock = new ReentrantLock(); // ...
public void m() { lock.lock(); // block until condition holds try { // ... method body }
finally {
lock.unlock() } } }
26
Synchronisation: Solution 2synchronized: réalise block, try [bloc], finally {unlock}
public class BankAccount{
public synchronized void deposit(double amount){
System.out.print("Depositing " + amount);double newBalance = balance + amount;System.out.println(", new balance is " + newBalance);balance = newBalance;
}
public synchronized void withdraw(double amount){
System.out.print("Withdrawing " + amount);double newBalance = balance - amount;System.out.println(", new balance is " + newBalance);balance = newBalance;
}…
27
Problème de Deadlock
• 2 ou plus Threads attendent qu’un autre thread débloque une ressource nécessaire
Possède A
Demande B
Possède C
Demande A
Possède B
Demande C
28
Une solution à Deadlock (façon 1)
• Objet Condition associé à un Lock
public class BankAccount{ public BankAccount() //Constructeur { balanceChangeLock = new ReentrantLock(); sufficientFundsCondition = balanceChangeLock.newCondition(); . . . } . . . private Lock balanceChangeLock; private Condition sufficientFundsCondition;}
29
Une solution à Deadlock
• Un retrait attend la condition nécessaire
public void withdraw(double amount){ balanceChangeLock.lock(); try { while (balance < amount) sufficientFundsCondition.await(); . . . } finally { balanceChangeLock.unlock(); }}
30
Effets de await()
sufficientFundsCondition.await();
• Attendre la ressource nécessaire (suffisamment d’argent)
• Relâche l’objet bloqué en attendant (BankAccount)– Permet aux autres threads d’exécuter
• L’attente se réveille quand il reçoit un signal – signalAll()
31
Une solution à Deadlock
• Un retrait attend la condition nécessaire
public void withdraw(double amount){ balanceChangeLock.lock(); try { while (balance < amount) sufficientFundsCondition.await(); . . . // faire le retrait } finally { balanceChangeLock.unlock(); }}
Le thread attend, relâche l’objet BankAcount, attend d’être signalé (ou interrompu)
Quand il est signalé, on doit tester encore la condition balance < amount
32
Une solution à Deadlock• Un dépôt signale à tous les Thread que la condition est
possiblement changée
public void deposit(double amount){
balanceChangeLock.lock();try{
System.out.print("Depositing " + amount);double newBalance = balance + amount;System.out.println(", new balance is " + newBalance);balance = newBalance;sufficientFundsCondition.signalAll();
}finally{
balanceChange.unlock();}
}
33
Condition
• Construction à partir d’un Lock:– Lock.newCondition()
• Méthodes de Condition:– void await(): attend d’être signalé ou
interrompu– void signal(): réveille un thread qui attend– void signalAll(): réveille tous les threads qui
attendent
34
Résuméclass Ressource{ Lock l;
Condition c = l.newCondition();
public void methode1 //consumer{ l.lock();
try { while (…) c.await(); … }finally {l.unlock();}
}
public void methode2 //producer{
l.lock();try { …, signalAll(); }finally {l.unclock();
}…}
Méthode qui attend une condition
Méthode qui peut satisfaire ne condition d’un autre thread
35
Sortir de Deadlock (façon 2)
• Thread.yield()– static void yield();– Pause du thread courant, et permet aux
autres threads d’exécuter
• Utilisation typique– Si le thread ne dispose qu’une partie de
resource, et l’autre partie n’est pas disponible:• relâcher la ressource retenu (la rendre disponible)• Thread.yield(); // permet aux autres d’exécuter
36
Les méthodes dans Object
• Les méthodes de Object permettent aussi de synchroniser les threads:– void wait(): attendre– void notify(): signale un Thread qui attend
qu’il y a eu un changement– void notifyAll(): signale à tous les Threads
37
Exemple de deadlock
• Les deux méthodes bloquent
public synchronized int get() { //Won't work!if (available == true) {
available = false;return contents;
}}
public synchronized void put(int value) { //Won't work!if (available == false) {
available = true;contents = value;
}}
38
Solution à Deadlock (façon 3)public synchronized int get() { while (available == false) { try { //Wait for Producer to put value. wait(); } catch (InterruptedException e) { } } available = false; //Notify Producer that value has been
retrieved. notifyAll(); return contents;}
public synchronized void put(int value) { while (available == true) { try { //Wait for Consumer to get value. wait(); } catch (InterruptedException e) { } } contents = value; available = true; //Notify Consumer that value has been
set. notifyAll();}
Tant que la condition n’est pas vraie, wait():
- met le thread en attente,
- permet aux autres threads d’exécuter
notifyAll(): réveille tous ceux qui wait()