Les threads en Java
Arnaud LabourelCourriel : [email protected]
Universite de Provence
26 janvier 2012
Arnaud Labourel, [email protected] Les threads en Java
Au Temps des Dinosaures...
et des cartes perforees, les ordinateurs etaient de grossesinstallations sur lesquelles on pouvait soumettre untravail puis venir recuperer le resultat quelques heuresplus tard.
Si un probleme se produisait en cours d’execution, on nele savait que plus tard et le temps affecte etait perdu...
Arnaud Labourel, [email protected] Les threads en Java
Multi-Taches
Le multi-taches permet de
optimiser l’utilisation des ressources (CPU, ...)
faciliter la programmation (modularisation)faciliter l’equite
Serveur multi-utilisateursStation mono-utilisateur : systeme plus “reactif”
Arnaud Labourel, [email protected] Les threads en Java
Processus et Threads
Une activite est une suite d’instructions (executionsequentielle.)
Un systeme est multi-tache s’il peut faire coexisterplusieurs activites au sein d’un meme environnement.
Un processus est une activite seule au sein d’unenvironnement restreint (memoire ...).
Au contraire un thread (ou processus leger) partage toutun environnement memoire avec d’autres threads.
Arnaud Labourel, [email protected] Les threads en Java
Exemples
Activites :
processus UNIX
threads POSIX
threads Java
Systemes d’exploitation :
Windows (NT, 2000, XP, Vista, seven)
SolarisUnix :
Les Unix proprietaires (AIX, HP UX, ...)BSDLinux (vrais threads depuis 2.6)Mac OS X
Arnaud Labourel, [email protected] Les threads en Java
Creation de Threads en Java
Deux manieres de faire :Classe Thread
methode run : code de l’activitemethode start : lancement de l’activitemethodes de controle ...=⇒par heritage de Thread.
interface Runnableune unique methode (abstraite) public void run() :code de l’activitepar implementation de Runnable+ constructeurs de la classe Thread
Arnaud Labourel, [email protected] Les threads en Java
Creation de Threads en Java
Deux manieres de faire :Classe Thread
methode run : code de l’activitemethode start : lancement de l’activitemethodes de controle ...=⇒par heritage de Thread.
interface Runnableune unique methode (abstraite) public void run() :code de l’activitepar implementation de Runnable+ constructeurs de la classe Thread
Arnaud Labourel, [email protected] Les threads en Java
Rappels : “Heritages Multiples”
Pourquoi deux manieres de faire ?
Rappel : Java n’autorise pas l’heritage multiple.
Une classe ne peut etre l’extension (ne peux heriter) qued’une seule classe.Par contre elle peut implementer plusieurs interfaces.Rappel : une interface n’a pas de variable d’instance etn’a que des methodes abstraites...
Arnaud Labourel, [email protected] Les threads en Java
Comment choisir entre les deux solutions ?
Heriter de Thread : quand on n’a pas besoind’heriter d’une autre classe.
Implementer Runnable : quand on souhaiteheriter d’une autre classe.
Arnaud Labourel, [email protected] Les threads en Java
Exemple : ExThread.javac l a s s ExThread extends Thread {
i n t i d ;
ExThread ( i n t v a l ) {/∗ c o n s t r u c t e u r ∗/i d = v a l ;}
/∗ Methode e x e c u t e e par l e t h r e a d ∗/publ ic void run ( ) {. . .}. . .
fo r ( i n t i =1; i<=n ; i ++)new ExThread ( i ) . s t a r t ( ) ;
. . .}
Arnaud Labourel, [email protected] Les threads en Java
Exemple : ExRunnable.javac l a s s ExRunnable implements Runnable {
i n t i d ;
ExThread ( i n t v a l ) {/∗ c o n s t r u c t e u r ∗/i d = v a l ;}
/∗ Methode e x e c u t e e par l e t h r e a d ∗/publ ic void run ( ) {. . .}. . .
fo r ( i n t i =1; i<=n ; i ++)new Thread (new ExRunnable ( i ) ) . s t a r t ( ) ;
. . .}
Arnaud Labourel, [email protected] Les threads en Java
Remarque Importante
Ne pas confondre l’activite (le thread : processus leger)avec la structure de donnees (le Thread : l’objet).
Arnaud Labourel, [email protected] Les threads en Java
Information sur les Threads
static int activeCount() : renvoie le nombre dethreads actuellement executes
static int enumerate(Thread[] tarray) :stocke l’ensemble des Threads du meme groupedans le tableau et renvoie le nombre de threads.
static Thread currentThread() : renvoie leThread correspondant au thread en train d’etreexecute. (utile notamment avec Runnable)
Arnaud Labourel, [email protected] Les threads en Java
Autres methodes sur les Threads
void setName(String name) : change le nom duThread
String getName() : retourne le nom du Thread
new Thread(ThreadGroup group,Runnable target , String name) : cree un
Thread dans un groupe.
new ThreadGroup(”Mon groupe”) : cree ungroupe de Thread
Arnaud Labourel, [email protected] Les threads en Java
Ordonnancement
L’ordonnanceur de la JVM n’est pas tenu d’etreequitable. Les threads de basses priorites ne sontexecutes que si les threads de hautes priorites sontbloques...
void setPriority ( int ) : fixer la priorite du thread
int getPriority () : renvoie la priorite du thread
static void yield () : provoque une “pause” duthread en cours et un reordonnancement (indicatifseulement...)
L’ordonnanceur n’est pas tenu de tenir compte despriorites...=⇒inutilisable de maniere portable.
Arnaud Labourel, [email protected] Les threads en Java
Manipulation des Threads
mise en attente :void sleep (long millis )void sleep (long millis , int nanos)
NB : pas de garantie temps-reelattente de l’arret d’un thread donne :
void join ()void join (long millis ) avec delaivoid join (long millis , int nanos) avec delai
interruption d’un thread :
void interrupt () : un thread “interrompt” (envoie uneInterruptedException ) un autre.
Arnaud Labourel, [email protected] Les threads en Java
Rappels : Exceptions
Certaines des methodes precedentes (bloquantes)demandent une gestion explicite des interruptions partry{ ... } catch(InterruptedException e){ ... }Une exception correspond en general a un deroulementanormal du programme. De plus, dans le casmulti-threade, cela peut correspondre a certainesinteractions entre threads.
Erreur inhabituelle =⇒gestion d’erreur classique
Declenchement d’une exception
Gestion des interruptions/exceptions.
Arnaud Labourel, [email protected] Les threads en Java
Throwable
unchecked exception : erreur engendree par la JVM(interaction systeme) =⇒ne doit pasnecessairement etre geree.
checked exception : erreur definie par l’utilisateur,doit necessairement etre geree (=⇒throws)
Ici, plus particulierement, InterruptedException .
Arnaud Labourel, [email protected] Les threads en Java
try, catch, finally
. . .t ry {/∗ a p p e l a une methode l a n c a n t des e x c e p t i o n s ∗/
. . .} catch ( TypeExcept ion e ){
. . .} catch ( A u t r e T y p e E x c e p t i o n e ){
. . .} f i n a l l y { // t o u j o u r s e x e c u t e
. . .}
Arnaud Labourel, [email protected] Les threads en Java
Variables Partagees
Les variables de la classe declarees en static sontpartagees entre tous les threads associes a cette classe.
Il est conseille de rajouter le mot clef volatile pourproteger la variable de certaines “optimisations” de laJVM.
Voir, plus tard, le package atomic, puis les questions devisibilite et du modele memoire Java.
Arnaud Labourel, [email protected] Les threads en Java
Synchronisation et Exclusion Mutuelle
Le mot-clef synchronized permet de gerer lasynchronisation des acces a la memoire partagee.
Un appel a une methode declaree synchronized d’uneinstance donnee est en exclusion mutuelle vis-a-vis detous les appels de methodes declarees synchronized surcette meme instance.
Arnaud Labourel, [email protected] Les threads en Java
Tres Important : Methodologie
Il faudra toujours bien separer donnees partagees etactivites=⇒au moins Deux classes.
Voir VariablePartagee.java,VariableSynchronized.java,VariableBienSynchronized.java.
Arnaud Labourel, [email protected] Les threads en Java
VariablePartagee.java
c l a s s E n t i e r P a r t a g e{
i n t v a l e u r ;
E n t i e r P a r t a g e ( i n t v a l e u r ){t h i s . v a l e u r = v a l e u r ;
}
v o i d decremente ( i n t dec ){v a l e u r −= dec ;
}
p u b l i c S t r i n g t o S t r i n g (){r e t u r n S t r i n g . v a l u e O f ( v a l e u r ) ;
}}
c l a s s V a r i a b l e P a r t a g e e extends Thread {s t a t i c v o l a t i l e E n t i e r P a r t a g e v a r p a r t ;i n t i d ;
V a r i a b l e P a r t a g e e ( i n t i d ) {t h i s . i d = i d ;
}
p u b l i c v o i d run ( ) {w h i l e ( v a r p a r t . v a l e u r >= i d ){
a f f i c h e ( ” Avant ”+v a r p a r t ) ;v a r p a r t . decremente ( i d ) ;a f f i c h e ( ” Apr e s ”+v a r p a r t ) ;t r y { s l e e p ( 0 , 0 ) ;}catch ( I n t e r r u p t e d E x c e p t i o n e ){ a f f i c h e ( ” I n t e r r o m p u ” ) ;}
}a f f i c h e ( ” Termine ( ”+v a r p a r t+” ) ” ) ;
}
v o i d a f f i c h e ( S t r i n g s ){System . out . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . getName ()+” : ”+s ) ;
}
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {i n t n = 3 , v a l =9;
v a r p a r t = new E n t i e r P a r t a g e ( v a l ) ;f o r ( i n t i =1; i<=n ; i ++)
new V a r i a b l e P a r t a g e e ( i ) . s t a r t ( ) ;System . out . p r i n t l n ( ”Main : v a l e u r = ”+v a l ) ;System . out . p r i n t l n ( ”Main : I l y a ”+n+” t h r e a d s , l a r e g l e du j e u e s t chaque t h r e a d ne ” ) ;System . out . p r i n t l n ( ”Main : peut r e t i r e r qu ’ un nombre de p o i n t s e g a l e a son i d e n t i t e .\n” ) ;
}}
Arnaud Labourel, [email protected] Les threads en Java
VariablePartageeSynchronized.java
c l a s s E n t i e r P a r t a g e{
i n t v a l e u r ;
E n t i e r P a r t a g e ( i n t v a l e u r ){t h i s . v a l e u r = v a l e u r ;
}
s y n c h r o n i z e d v o i d decremente ( i n t dec ){v a l e u r −= dec ;
}
p u b l i c S t r i n g t o S t r i n g (){r e t u r n S t r i n g . v a l u e O f ( v a l e u r ) ;
}}
c l a s s V a r i a b l e P a r t a g e e Sextends Thread {
s t a t i c E n t i e r P a r t a g e v a r p a r t ;i n t i d ;
V a r i a b l e P a r t a g e e B S ( i n t i d ) {t h i s . i d = i d ;
}
p u b l i c v o i d run ( ) {w h i l e ( v a r p a r t . v a l e u r>=i d ){
a f f i c h e ( ” Avant ”+v a r p a r t ) ;v a r p a r t . decremente ( i d ) ;a f f i c h e ( ” Apr e s ”+v a r p a r t ) ;t r y { s l e e p ( 0 , 0 ) ;}catch ( I n t e r r u p t e d E x c e p t i o n e ){a f f i c h e ( ” I n t e r r o m p u ” ) ;
}}a f f i c h e ( ” Termine ( ”+v a r p a r t+” ) ” ) ;}
v o i d a f f i c h e ( S t r i n g s ){System . out . p r i n t l n (
Thread . c u r r e n t T h r e a d ( ) . getName ()+” : ”+s ) ;}
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {i n t n = 3 , v a l =9;
v a r p a r t = new E n t i e r P a r t a g e ( v a l ) ;f o r ( i n t i =1; i<=n ; i ++)
new V a r i a b l e P a r t a g e e S ( i ) . s t a r t ( ) ;System . out . p r i n t l n ( ”Main : v a l e u r = ”+v a l ) ;System . out . p r i n t l n ( ”Main : I l y a”+n+” t h r e a d s , ”
+” l a r e g l e du j e u e s t chaque t h r e a d ne ” ) ;System . out . p r i n t l n ( ”Main : peut r e t i r e r qu ’ un”
+” nombre de p o i n t s e g a l e a son i d e n t i t e .\n” ) ;}
Arnaud Labourel, [email protected] Les threads en Java
VariablePartageeBienSynchronized.javac l a s s E n t i e r P a r t a g e{
i n t v a l e u r ;
E n t i e r P a r t a g e ( i n t v a l e u r ){t h i s . v a l e u r = v a l e u r ;
}
s y n c h r o n i z e d v o i d decremente ( i n t dec ){v a l e u r −= dec ;
}
s y n c h r o n i z e d boolean t e s t e ( i n t v ){r e t u r n ( v a l e u r>=v ) ;
}
p u b l i c S t r i n g t o S t r i n g (){r e t u r n S t r i n g . v a l u e O f ( v a l e u r ) ;
}}
c l a s s V a r i a b l e P a r t a g e e B Sextends Thread {
s t a t i c E n t i e r P a r t a g e v a r p a r t ;i n t i d ;
V a r i a b l e P a r t a g e e B S ( i n t i d ) {t h i s . i d = i d ;
}
p u b l i c v o i d run ( ) {w h i l e ( v a r p a r t . t e s t e ( i d ) ){
a f f i c h e ( ” Avant ”+v a r p a r t ) ;v a r p a r t . decremente ( i d ) ;a f f i c h e ( ” Apr e s ”+v a r p a r t ) ;t r y { s l e e p ( 0 , 0 ) ;}catch ( I n t e r r u p t e d E x c e p t i o n e ){a f f i c h e ( ” I n t e r r o m p u ” ) ;
}}a f f i c h e ( ” Termine ( ”+v a r p a r t+” ) ” ) ;}
v o i d a f f i c h e ( S t r i n g s ){System . out . p r i n t l n (
Thread . c u r r e n t T h r e a d ( ) . getName ()+” : ”+s ) ;}
p u b l i c s t a t i c v o i d main ( S t r i n g [ ] a r g s ) {i n t n = 3 , v a l =9;
v a r p a r t = new E n t i e r P a r t a g e ( v a l ) ;f o r ( i n t i =1; i<=n ; i ++)
new V a r i a b l e P a r t a g e e B S ( i ) . s t a r t ( ) ;System . out . p r i n t l n ( ”Main : v a l e u r = ”+v a l ) ;System . out . p r i n t l n ( ”Main : I l y a”+n+” t h r e a d s , ”
+” l a r e g l e du j e u e s t chaque t h r e a d ne ” ) ;System . out . p r i n t l n ( ”Main : peut r e t i r e r qu ’ un”
+” nombre de p o i n t s e g a l e a son i d e n t i t e .\n” ) ;}
Arnaud Labourel, [email protected] Les threads en Java
Problemes de Coexistence
L’existence de plusieurs activites au sein d’un memeenvironnement induit necessairement des problemes de
ordonnancement =⇒non-determinisme desexecutionspartage des donnees
visibilite des donneessynchronisation des actions sur ces donnees
Arnaud Labourel, [email protected] Les threads en Java
Conclusion ProvisoireLa programmation multi-thread est delicate :
non-determinisme,variables partagee et synchronisation,optimisations provoquant des comportementsparfois non intuitifs,portabilite vs utilisation des ressources natives,variations dans l’implementation des machinesvirtuelles,erreurs ne se revelant que sous hautes charges (ie enproduction)...
Methodologie particuliere a suivre en utilisant lesnombreuses primitives de synchronisation (apparuesdepuis la version 1.5).
Arnaud Labourel, [email protected] Les threads en Java