as Programmation - Threads - as Principe des threads Créer un

Transcription

as Programmation - Threads - as Principe des threads Créer un
Gestion des threads Gestion des timers
Gestion des threads Gestion des timers
1
Gestion des threads
Principe
Création
Cycle
Synchronisation
Collections
2
Gestion des timers
Principe
Création
Exemple
Programmation
- Threads Nicolas Malandain
May 17, 2005
as
as
Architecture des Systèmes d’Information
Threads
Gestion des threads Gestion des timers
Architecture des Systèmes d’Information
1 / 32
Principe Création Cycle Synchronisation Collections
Threads
Gestion des threads Gestion des timers
Principe des threads
2 / 32
Principe Création Cycle Synchronisation Collections
Créer un thread avec la classe Thread
programme
programme
thread
Description partielle
public class Thread implements Runnable {
public Thread()
public Thread(Runnable target)
public Thread(String name)
...
public void setDaemon(boolean on) // la JVM se termine s’il ne reste que des démons
public void run()
public void start()
...
}
threads
Créer un thread
hériter de Thread
Définition
Un thread correspond à l’exécution d’une séquence d’instructions dans un
programme. Plusieurs threads peuvent s’exécuter dans un programme, ils
partagent alors les ressources de ce programme.
Threads
redéfinir la méthode run (contient le code exécuté par le thread)
instancier la classe
lancer le thread (start)
3 / 32
cm07-thread.pdf — May 17, 2005 — 1
Threads
4 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Créer un thread avec l’interface Runnable
CompteurThread.java
public class CompteurThread extends Thread {
private int bornemax;
Exécutions
public CompteurThread(String nom, int bornemax) {
super(nom);
this .bornemax = bornemax;
}
public void run() {
for (int i=0; i<bornemax; i++) {
System.out.println(getName()+" : "+i);
try {
sleep(100);
} catch (InterruptedException e) { }
}
}
public static void main(String[] args) {
CompteurThread ct1 = new CompteurThread("A",10);
CompteurThread ct2 = new CompteurThread("B",10);
ct1.start();
ct2.start();
ct1.interrupt();
}
A
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
B
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
0
1
0
2
1
3
2
4
3
5
4
6
5
7
6
8
7
9
8
9
B
A
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
A
B
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
:
Description
0
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
public interface Runnable {
public void run()
}
L’interface Runnable permet de créer des threads lorsque l’héritage n’est
pas possible
Créer un thread
implémeter l’interface Runnable
définir la méthode run (contient le code exécuté par le thread)
instancier un thread avec un objet Runnable instancié
lancer le thread (start)
}
Threads
Gestion des threads Gestion des timers
Threads
5 / 32
Principe Création Cycle Synchronisation Collections
6 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Cycle de vie d’un thread
CompteurThreadRunnable.java
public class CompteurThreadRunnable implements Runnable {
private int bornemax;
public CompteurThreadRunnable(int bornemax) {
this .bornemax = bornemax;
}
public void run() {
Thread monThread = Thread.currentThread();
for (int i=0; i<bornemax; i++) {
System.out.println(monThread.getName()+" : "+i);
try {
monThread.sleep(100);
} catch (InterruptedException e) { }
}
}
Exécutable
créé
(actif)
start
prêt
déblocage IO
notify, interrupt
public Thread go(String nom) {
Thread thread = new Thread(this,nom);
thread.start();
return thread;
}
public static void main(String[] args) {
CompteurThreadRunnable ctr1 = new CompteurThreadRunnable(10);
CompteurThreadRunnable ctr2 = new CompteurThreadRunnable(10);
ctr1.go("A");
ctr2.go("B");
}
fin du code
terminé
élu
attente d'IO
wait, join, sleep
bloqué
(synchro, sleep, IO, ...)
}
Threads
7 / 32
cm07-thread.pdf — May 17, 2005 — 2
Threads
8 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Arrêter un thread
Principe Création Cycle Synchronisation Collections
CompteurThreadArret.java
Méthode
Un thread doit se terminer naturellement de lui même. Le plus simple est
un changement d’état interne donnant fin à la méthode run.
Attention !!!
La méthode stop n’est plus à utiliser (non supportée dans les futures
versions), elle ne permettait pas une fin propre du thread.
Threads
Gestion des threads Gestion des timers
9 / 32
Principe Création Cycle Synchronisation Collections
CompteurThreadCourantArret.java
Gestion des threads Gestion des timers
...
public class CompteurThreadCourantArret implements Runnable {
private int pas;
private int compteur;
private Thread thisThread;
private boolean etat=true;
10 / 32
Principe Création Cycle Synchronisation Collections
CompteurThreadCourantArret.java
public static void main(String[] args) {
CompteurThreadCourantArret ct1 = new CompteurThreadCourantArret(1);
Thread t1 = new Thread(ct1,"A");
t1.start();
Scanner input = new Scanner(System.in);
String reponse;
do {
System.out.println("Lancement du 2eme thread ? O/N ");
reponse = input.next();
} while (!reponse.toUpperCase().equals("O"));
Thread t2 = new Thread(ct1,"B");
t2.start();
do {
System.out.println("Arr^
eter le thread ? O/N");
reponse = input.next();
} while (!reponse.toUpperCase().equals("O"));
ct1.stop();
}
public CompteurThreadCourantArret(int pas) {
setPas(pas);
}
public void setPas(int pas) {
this .pas = pas;
}
public void stop() {
thisThread=null;
}
public void run() {
compteur = 0;
thisThread = Thread.currentThread();
while (thisThread == Thread.currentThread()) {
compteur += pas;
System.out.println(thisThread.getName()+" : "+compteur);
try {
thisThread.sleep(100);
} catch (InterruptedException e) { }
}
}
Threads
public class CompteurThreadArret extends Thread {
private int compteur;
private boolean etat=true;
public CompteurThreadArret(String nom, int pas) {
super(nom);
setPas(pas);
}
public void setFin() {
etat=false;
}
public void run() {
while (etat) {
compteur += pas;
System.out.println(getName()+" : "+compteur);
try {
sleep(100);
} catch (InterruptedException e) { }
}
}
public static void main(String[] args) {
CompteurThreadArret ct1 = new CompteurThreadArret("A",1);
ct1.start();
Scanner input = new Scanner(System.in);
String reponse;
do {
System.out.println("Voulez vous arr^
eter le thread ? O/N");
reponse = input.next();
} while (!reponse.toUpperCase().equals("O"));
ct1.setFin();
}
}
Threads
Dans cet exemple, un seul et unique thread peut être lancé sur le même
objet ct1. Le changement d’état de ct1 (stop) ou le lancement d’un
nouveau thread arrête le thread.
11 / 32
cm07-thread.pdf — May 17, 2005 — 3
Threads
12 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Principe
Principe Création Cycle Synchronisation Collections
Exemples des producteurs/consommateurs 1
ProducteurConsommateurPBAL.java
Rappel du problème
Plusieurs threads veulent accéder en même temps à une ressource
partagée critique. L’accès à cette ressource doit être exclusif.
public class ProducteurConsommateurPBAL {
public static void main(String[] args) {
PetiteBoiteAuxLettres pbal = new PetiteBoiteAuxLettres();
ProducteurPBAL p1 = new ProducteurPBAL(pbal, "lettre", 10);
ProducteurPBAL p2 = new ProducteurPBAL(pbal, "lettre", 10);
ConsommateurPBAL c1 = new ConsommateurPBAL(pbal);
Thread t1 = new Thread(p1, "P1");
t1.setPriority(1);
t1.start();
Thread t2 = new Thread(p2, "P2");
t2.setPriority(10);
t2.start();
new Thread(c1, "C1").start();
}
Java ⇒ synchronized
le modificateur synchronized sur une méthode vérouille (lock) l’objet
lors de la réception du message et le dévérouille à la fin de l’exécution de
la méthode.
}
Threads
Gestion des threads Gestion des timers
13 / 32
Threads
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
14 / 32
Principe Création Cycle Synchronisation Collections
ProducteurPBAL.java
PetiteBoiteAuxLettres.java
public class ProducteurPBAL implements Runnable {
private PetiteBoiteAuxLettres pbal;
private String prefixe;
private int nbLettres;
/** Creates a new instance of ProducteurPBAL */
public ProducteurPBAL(PetiteBoiteAuxLettres pbal, String prefixe, int nbLettres) {
this .pbal = pbal;
this .prefixe = prefixe;
this .nbLettres = nbLettres;
}
public class PetiteBoiteAuxLettres {
private String lettre;
private boolean nouvelleValeurDisponible = false;
public synchronized void put(String lettre) throws InterruptedException {
while (nouvelleValeurDisponible)
wait();
System.out.println(lettre+" -> boite");
nouvelleValeurDisponible = true;
this .lettre = lettre;
notifyAll();
}
public synchronized String get() throws InterruptedException {
while (!nouvelleValeurDisponible)
wait();
System.out.println("boite -> "+lettre);
nouvelleValeurDisponible = false;
notifyAll();
return lettre;
}
public String produire() {
String lettre = prefixe+" : "+(int)(Math.random()*100);
System.out.println(Thread.currentThread().getName()+" envoie "+lettre);
return lettre;
}
public void run() {
try {
for (int i=0; i < nbLettres; i++)
pbal.put(produire());
} catch (InterruptedException ie) {}
}
}
}
Threads
15 / 32
cm07-thread.pdf — May 17, 2005 — 4
Threads
16 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Exécution
P1 envoie lettre-P1 : 73
lettre-P1 : 73 -> boite
P1 envoie lettre-P1 : 16
P2 envoie lettre-P2 : 73
boite -> lettre-P1 : 73
C1 reçoit lettre-P1 : 73
lettre-P2 : 73 -> boite
P2 envoie lettre-P2 : 90
boite -> lettre-P2 : 73
C1 reçoit lettre-P2 : 73
lettre-P2 : 90 -> boite
P2 envoie lettre-P2 : 60
boite -> lettre-P2 : 90
C1 reçoit lettre-P2 : 90
lettre-P2 : 60 -> boite
P2 envoie lettre-P2 : 1
boite -> lettre-P2 : 60
C1 reçoit lettre-P2 : 60
lettre-P2 : 1 -> boite
P2 envoie lettre-P2 : 53
boite -> lettre-P2 : 1
C1 reçoit lettre-P2 : 1
lettre-P2 : 53 -> boite
P2 envoie lettre-P2 : 19
boite -> lettre-P2 : 53
C1 reçoit lettre-P2 : 53
lettre-P2 : 19 -> boite
ConsommateurPBAL.java
public class ConsommateurPBAL implements Runnable {
private PetiteBoiteAuxLettres pbal;
public ConsommateurPBAL(PetiteBoiteAuxLettres pbal) {
this .pbal = pbal;
}
public void consommer(String lettre) {
System.out.println(Thread.currentThread().getName()+" reçoit "+lettre);
}
public void run() {
try {
while (true)
consommer(pbal.get());
} catch (InterruptedException ie) {}
}
}
Threads
Gestion des threads Gestion des timers
17 / 32
P2 envoie lettre-P2 : 57
boite -> lettre-P2 : 19
C1 reçoit lettre-P2 : 19
lettre-P2 : 57 -> boite
P2 envoie lettre-P2 : 14
boite -> lettre-P2 : 57
C1 reçoit lettre-P2 : 57
lettre-P2 : 14 -> boite
P2 envoie lettre-P2 : 63
boite -> lettre-P2 : 14
C1 reçoit lettre-P2 : 14
lettre-P2 : 63 -> boite
P2 envoie lettre-P2 : 95
boite -> lettre-P2 : 63
C1 reçoit lettre-P2 : 63
lettre-P2 : 95 -> boite
boite -> lettre-P2 : 95
C1 reçoit lettre-P2 : 95
lettre-P1 : 16 -> boite
P1 envoie lettre-P1 : 5
boite -> lettre-P1 : 16
C1 reçoit lettre-P1 : 16
lettre-P1 : 5 -> boite
P1 envoie lettre-P1 : 67
boite -> lettre-P1 : 5
C1 reçoit lettre-P1 : 5
lettre-P1 : 67 -> boite
P1 envoie lettre-P1 : 7
boite -> lettre-P1 : 67
C1 reçoit lettre-P1 : 67
lettre-P1 : 7 -> boite
P1 envoie lettre-P1 : 93
boite -> lettre-P1 : 7
C1 reçoit lettre-P1 : 7
lettre-P1 : 93 -> boite
P1 envoie lettre-P1 : 24
boite -> lettre-P1 : 93
C1 reçoit lettre-P1 : 93
lettre-P1 : 24 -> boite
P1 envoie lettre-P1 : 22
boite -> lettre-P1 : 24
C1 reçoit lettre-P1 : 24
lettre-P1 : 22 -> boite
P1 envoie lettre-P1 : 21
boite -> lettre-P1 : 22
C1 reçoit lettre-P1 : 22
lettre-P1 : 21 -> boite
P1 envoie lettre-P1 : 47
boite -> lettre-P1 : 21
C1 reçoit lettre-P1 : 21
lettre-P1 : 47 -> boite
boite -> lettre-P1 : 47
C1 reçoit lettre-P1 : 47
Threads
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
18 / 32
Principe Création Cycle Synchronisation Collections
Exemples des producteurs/consommateurs 2
ProducteurBAL.java
public class ProducteurBAL implements Runnable {
private LinkedBlockingQueue<String> bal;
private String prefixe;
private int nbLettres;
public ProducteurBAL(LinkedBlockingQueue<String> bal, String prefixe, int nbLettres) {
this .bal = bal;
this .prefixe = prefixe;
this .nbLettres = nbLettres;
}
ProducteurConsommateurBAL.java
public class ProducteurConsommateurBAL {
public static void main(String[] args) {
// boite aux lettres de taille 3
LinkedBlockingQueue<String> bal = new LinkedBlockingQueue<String>(3);
ProducteurBAL p1 = new ProducteurBAL(bal, "lettre-P1", 5);
ProducteurBAL p2 = new ProducteurBAL(bal, "lettre-P2", 5);
ConsommateurBAL c1 = new ConsommateurBAL(bal);
new Thread(p1, "P1").start();
new Thread(p2, "P2").start();
new Thread(c1, "C1").start();
}
public String produire() {
String lettre = prefixe+" : "+(int)(Math.random()*100);
System.out.println(Thread.currentThread().getName()+" envoie "+lettre);
return lettre;
}
public void run() {
try {
for (int i=0; i < nbLettres; i++)
bal.put(produire());
} catch (InterruptedException ie) {}
}
}
}
Threads
19 / 32
cm07-thread.pdf — May 17, 2005 — 5
Threads
20 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Vérouillage (Lock) réentrant
Définition
Un thread possédant un verrou sur un objet peut ré-accéder sans
problème à cette objet via une méthode requérant un verrou sur ce même
objet.
ConsommateurBAL.java
public class ConsommateurBAL implements Runnable {
private LinkedBlockingQueue<String> bal;
public ConsommateurBAL(LinkedBlockingQueue<String> bal) {
this .bal = bal;
}
Les vérouillages de Java sont réentrants, ce qui évite les “deadlocks”
public void consommer(String lettre) {
System.out.println(Thread.currentThread().getName()+" reçoit "+lettre);
}
Exemple de deadlock, si les vérrouillages ne sont pas réentrants
public void run() {
try {
while (true)
consommer(bal.take());
} catch (InterruptedException ie) {}
}
public class ObjetCritique {
public synchronized void methode1() {
methode2();
System.out.println("methode1()");
}
public synchronized void methode2() {
System.out.println("methode2");
}
}
}
Threads
Gestion des threads Gestion des timers
Threads
21 / 32
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Gestion des threads via Object
22 / 32
Principe Création Cycle Synchronisation Collections
Synchronisation d’une partie de code
wait(), wait(long timeout), wait(long timeout, int nanos)
Met le thread courant en attente et relâche le verrou.
Le thread attend jusqu’à ce qu’une des 2 possibilités soit rencontrée :
Syntaxe
le thread est réveillé via un notify ou notifyAll
le temps d’attente est écoulé (sauf wait())
synchronized (objet) {
// code critique
}
timeout : millisecondes
nanos : nanosecondes
Le thread pose le verrou sur l’objet.
Le thread repart une fois qu’il réobtient le verrou.
notify(), notifyAll()
Réveille le ou les threads en attente sur l’objet.
Threads
23 / 32
cm07-thread.pdf — May 17, 2005 — 6
Threads
24 / 32
Gestion des threads Gestion des timers
Principe Création Cycle Synchronisation Collections
Gestion des threads Gestion des timers
Synchronisation et Collections
Principe Création Cycle Synchronisation Collections
Création des wrappers de synchronisation avec Collections
public
public
public
public
public
public
Attention
La plupart des collections ne sont pas sures au niveau des threads
Quelques collections sures
la classe Vector
static
static
static
static
static
static
<E>
Collection<E> synchronizedCollection(Collection<E> c);
<E>
List<E>
synchronizedList(List<E> list);
<K, V> Map<K, V>
synchronizedMap(Map<K, V> m);
<E>
Set<E>
synchronizedSet(Set<E> s);
<K,V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m);
<E>
SortedSet<E> synchronizedSortedSet(SortedSet<E> s);
Remarques
La syntaxe suivante permet de ne garder la référence que sur la liste
synchronisée afin d’éviter les erreurs.
les classes implémentant BlockingQueue
...
List<Type> list;
list = Collections.synchronizedList(new LinkedList<Type>());
Utilisation des Wrappers de collections
Les wrappers ajoutent des fonctionnalités supplémentaires aux méthodes
des collections (design pattern de décoration).
Les wrappers de synchronisation permettent d’ajouter la synchronisation
sur les collections.
Threads
Gestion des threads Gestion des timers
L’itération (avec ou sans itérateur) sur les collections doit être
effectuée manuellement
synchronized(list)
for (Type elt : list)
toto(e);
Threads
25 / 32
Principe Création Exemple
Gestion des threads Gestion des timers
Timer
Timer
Description
Les timers permettent de planifier l’exécution de tâches.
API de la classe Timer
26 / 32
Principe Création Exemple
Timer()
Timer(boolean isDaemon)
Timer(String name)
Timer(String name, boolean isDaemon)
void cancel()
void schedule(TimerTask task, Date time)
void schedule(TimerTask task, Date firstTime, long period)
void schedule(TimerTask task, long delay)
void schedule(TimerTask task, long delay, long period)
// méthode précise ne tenant pas compte des retards
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
void scheduleAtFixedRate(TimerTask task, long delay, long period)
lancement à une date donnée
répétition à intervalle régulier
les tâches sont des threads
Fonctionnement
un Timer est toujours associé à un Thread
l’exécution du thread est programmée via les méthodes schedule
la tâche a exécuter est créée par héritage de TimerTask (Runnable)
Threads
27 / 32
cm07-thread.pdf — May 17, 2005 — 7
Threads
28 / 32
Gestion des threads Gestion des timers
Principe Création Exemple
Gestion des threads Gestion des timers
Principe Création Exemple
TimerTask
...
RappellerRDV.java
public static void main(String[] args) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date = sdf.parse(args[1]);
RappellerRDV rdv = new RappellerRDV(args[0],
date,
Integer.parseInt(args[2]),
Integer.parseInt(args[3]));
}
catch (Exception e) {
System.out.println("Usage : java RappellerRDV \"message\" \"dd/MM/yyyy HH:mm:ss\"
rappelnbminutesavant nbrappels");
}
}
La classe TimerTask
public class TimerTask implements Thread {
boolean cancel() // annule la t^
ache
abstract void run() // méthode à redéfinir pour l’exécution de la t^
ache
long scheduledExecutionTime() // rertourne la date de dernière exécution
}
Création d’une tâche
Une tâche se définit par héritage de TimerTask et redéfinition de la
méthode run()
Threads
Gestion des threads Gestion des timers
Exécution
java fr.insarouen.asi.prog.cours7.RappellerRDV "Hello" "17/05/2005 14:47:10" 10 3
(rappel) 17 mai 2005 14:47:10 : Hello
(rappel) 17 mai 2005 14:47:10 : Hello
(rappel) 17 mai 2005 14:47:10 : Hello
17 mai 2005 14:47:10 : Hello
29 / 32
Principe Création Exemple
Threads
Gestion des threads Gestion des timers
30 / 32
Principe Création Exemple
RappellerRDV.java (2 classes internes)
...
RappellerRDV.java
public class RappellerRDV {
private Timer timer;
private DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.MEDIUM,
Locale.FRANCE);
public RappellerRDV(String texte, Date date, int nbminutesavant, int nbfois) {
timer = new Timer();
timer.schedule(new RendezVous(texte, date), date);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
// pour tester remplacer MINUTE/SECOND et 60000/1000
cal.add(Calendar.MINUTE,-1*nbminutesavant);
timer.schedule(new Rappel(texte, date, nbfois),
cal.getTime(),
(60000*nbminutesavant)/nbfois);
}
Threads
private class RendezVous extends TimerTask {
private String texte;
private Date date;
public RendezVous(String texte, Date date) {
this .texte = texte;
this .date = date;
}
public void run() {
System.out.println(dateFormat.format(date)+" : "+texte);
timer.cancel();
}
}
private class Rappel extends TimerTask {
private String texte;
private Date date;
private int nbfois;
public Rappel(String texte, Date date, int nbfois) {
this .texte = texte;
this .date = date;
this .nbfois = nbfois;
}
public void run() {
System.out.println("(rappel) "+dateFormat.format(date)+" : "+texte);
if (nbfois-- <= 0)
cancel();
}
}
31 / 32
cm07-thread.pdf — May 17, 2005 — 8
Threads
32 / 32