Les threads en Java

Transcription

Les threads en Java
Les threads en Java
Arnaud Labourel
Courriel : [email protected]
Université de Provence
26 janvier 2012
Arnaud Labourel, [email protected]
Les threads en Java
Au Temps des Dinosaures...
et des cartes perforées, les ordinateurs étaient de grosses
installations sur lesquelles on pouvait soumettre un
travail puis venir récupérer le résultat quelques heures
plus tard.
Si un problème se produisait en cours d’exécution, on ne
le savait que plus tard et le temps affecté était perdu...
Arnaud Labourel, [email protected]
Les threads en Java
Multi-Tâches
Le multi-tâches permet de
optimiser l’utilisation des ressources (CPU, ...)
faciliter la programmation (modularisation)
faciliter l’équité
Serveur multi-utilisateurs
Station mono-utilisateur : système plus “réactif”
Arnaud Labourel, [email protected]
Les threads en Java
Processus et Threads
Une activité est une suite d’instructions (exécution
séquentielle.)
Un système est multi-tâche s’il peut faire coexister
plusieurs activités au sein d’un même environnement.
Un processus est une activité seule au sein d’un
environnement restreint (mémoire ...).
Au contraire un thread (ou processus léger) partage tout
un environnement mémoire avec d’autres threads.
Arnaud Labourel, [email protected]
Les threads en Java
Exemples
Activités :
processus UNIX
threads POSIX
threads Java
Systèmes d’exploitation :
Windows (NT, 2000, XP, Vista, seven)
Solaris
Unix :
Les Unix propriétaires (AIX, HP UX, ...)
BSD
Linux (vrais threads depuis 2.6)
Mac OS X
Arnaud Labourel, [email protected]
Les threads en Java
Création de Threads en Java
Deux manières de faire :
Classe Thread
méthode run : code de l’activité
méthode start : lancement de l’activité
méthodes de contrôle ...
=⇒par héritage de Thread.
Arnaud Labourel, [email protected]
Les threads en Java
Création de Threads en Java
Deux manières de faire :
Classe Thread
méthode run : code de l’activité
méthode start : lancement de l’activité
méthodes de contrôle ...
=⇒par héritage de Thread.
interface Runnable
une unique méthode (abstraite) public void run() :
code de l’activité
par implémentation de Runnable
+ constructeurs de la classe Thread
Arnaud Labourel, [email protected]
Les threads en Java
Rappels : “Héritages Multiples”
Pourquoi deux manières de faire ?
Rappel : Java n’autorise pas l’héritage multiple.
Une classe ne peut être l’extension (ne peux hériter) que
d’une seule classe.
Par contre elle peut implémenter plusieurs interfaces.
Rappel : une interface n’a pas de variable d’instance et
n’a que des méthodes abstraites...
Arnaud Labourel, [email protected]
Les threads en Java
Comment choisir entre les deux solutions ?
Hériter de Thread : quand on n’a pas besoin
d’hériter d’une autre classe.
Implémenter Runnable : quand on souhaite
hériter d’une autre classe.
Arnaud Labourel, [email protected]
Les threads en Java
Exemple : ExThread.java
c l a s s ExThread extends Thread {
int id ;
ExThread ( i n t v a l ) { /∗ c o n s t r u c t e u r ∗/
id = val ;
}
/∗ Méthode e x é c u t é e p a r l e t h r e a d ∗/
p u b l i c void r u n ( ) {
...
}
...
f o 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.java
c l a s s ExRunnable implements R u n n a b l e {
int id ;
ExThread ( i n t v a l ) { /∗ c o n s t r u c t e u r ∗/
id = val ;
}
/∗ Méthode e x é c u t é e p a r l e t h r e a d ∗/
p u b l i c void r u n ( ) {
...
}
...
f o 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’activité (le thread : processus léger)
avec la structure de données (le Thread : l’objet).
Arnaud Labourel, [email protected]
Les threads en Java
Information sur les Threads
static int activeCount() : renvoie le nombre de
threads actuellement exécutés
static int enumerate(Thread[] tarray ) :
stocke l’ensemble des Threads du même groupe
dans le tableau et renvoie le nombre de threads.
static Thread currentThread() : renvoie le
Thread correspondant au thread en train d’être
exécuté. (utile notamment avec Runnable)
Arnaud Labourel, [email protected]
Les threads en Java
Autres méthodes sur les Threads
void setName(String name) : change le nom du
Thread
String getName() : retourne le nom du Thread
new Thread(ThreadGroup group,
Runnable target , String name) : crée un
Thread dans un groupe.
new ThreadGroup(”Mon groupe”) : crée un
groupe de Thread
Arnaud Labourel, [email protected]
Les threads en Java
Ordonnancement
L’ordonnanceur de la JVM n’est pas tenu d’être
équitable. Les threads de basses priorités ne sont
exécutés que si les threads de hautes priorités sont
bloqués...
void setPriority ( int ) : fixer la priorité du thread
int getPriority () : renvoie la priorité du thread
static void yield () : provoque une “pause” du
thread en cours et un réordonnancement (indicatif
seulement...)
L’ordonnanceur n’est pas tenu de tenir compte des
priorités...
=⇒inutilisable de manière 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-réel
attente de l’arrêt d’un thread donné :
void join ()
void join (long millis ) avec délai
void join (long millis , int nanos) avec délai
interruption d’un thread :
void interrupt () : un thread “interrompt” (envoie une
InterruptedException ) un autre.
Arnaud Labourel, [email protected]
Les threads en Java
Rappels : Exceptions
Certaines des méthodes précédentes (bloquantes)
demandent une gestion explicite des interruptions par
try{ ... } catch(InterruptedException e){ ... }
Une exception correspond en général à un déroulement
anormal du programme. De plus, dans le cas
multi-threadé, cela peut correspondre à certaines
interactions entre threads.
Erreur inhabituelle =⇒gestion d’erreur classique
Déclenchement d’une exception
Gestion des interruptions/exceptions.
Arnaud Labourel, [email protected]
Les threads en Java
Throwable
unchecked exception : erreur engendrée par la JVM
(interaction système) =⇒ne doit pas
nécessairement être gérée.
checked exception : erreur définie par l’utilisateur,
doit nécessairement être gérée (=⇒throws)
Ici, plus particulièrement, InterruptedException .
Arnaud Labourel, [email protected]
Les threads en Java
try, catch, finally
...
try {
/∗ a p p e l à une méthode l a n ç a n t d e s e x c e p t i o n s ∗/
...
} catch ( T y p e E x c e p t i o n 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 é c u t é
...
}
Arnaud Labourel, [email protected]
Les threads en Java
Variables Partagées
Les variables de la classe déclarées en static sont
partagées entre tous les threads associés à cette classe.
Il est conseillé de rajouter le mot clef volatile pour
protéger la variable de certaines “optimisations” de la
JVM.
Voir, plus tard, le package atomic, puis les questions de
visibilité et du modèle mémoire Java.
Arnaud Labourel, [email protected]
Les threads en Java
Synchronisation et Exclusion Mutuelle
Le mot-clef synchronized permet de gérer la
synchronisation des accès à la mémoire partagée.
Un appel à une méthode déclarée synchronized d’une
instance donnée est en exclusion mutuelle vis-à-vis de
tous les appels de méthodes déclarées synchronized sur
cette même instance.
Arnaud Labourel, [email protected]
Les threads en Java
Très Important : Méthodologie
Il faudra toujours bien séparer données partagées et
activités
=⇒au moins Deux classes.
Voir VariablePartagee.java,
VariableSynchronized.java,
VariableBienSynchronized.java.
Arnaud Labourel, [email protected]
Les threads en Java
VariablePartagee.java
class EntierPartage
{
int valeur ;
E n t i e r P a r t a g e ( i n t v a l e u r ){
this . valeur = valeur ;
}
v o i d d e c r e m e n t e ( i n t d e c ){
v a l e u r −= d e c ;
p u b l i c void 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 ( ” A p rè s ”+v a r p a r t ) ;
try {sleep (0 ,0);}
c a t c h ( 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
}
}
a f f i c h e ( ” Terminé ( ”+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 . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) .
}
p u b l i c S t r i n g t o S t r i n g (){
return String . valueOf ( valeur ) ;
}
p u b l i c s t a t i c v o i d main ( S t r i n g [ ]
i n t n = 3 , v a l =9;
}
c l a s s V a r i a b l e P a r t a g e e e x t e n d s Thread {
static v o l a t i l e EntierPartage varpart ;
int id ;
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 . o u t . p r i n t l n ( ” Main : v a l e u r = ”+v a l )
System . o u t . p r i n t l n ( ” Main : I l y a ”+n+” t h
System . o u t . p r i n t l n ( ” Main : p e u t r e t i r e r qu ’
VariablePartagee ( int id ) {
this . id = id ;
}
}
}
Arnaud Labourel, [email protected]
args ) {
Les threads en Java
VariablePartageeSynchronized.java
class EntierPartage
{
int valeur ;
E n t i e r P a r t a g e ( i n t v a l e u r ){
this . valeur = valeur ;
}
s y n c h r o n i z e d v o i d d e c r e m e n t e ( i n t d e c ){
v a l e u r −= d e c ;
}
p u b l i c S t r i n g t o S t r i n g (){
return String . valueOf ( valeur ) ;
}
}
class VariablePartageeS
e x t e n d s Thread {
static EntierPartage varpart ;
int id ;
p u b l i c void 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 ( ” A p rè s ”+v a r p a r t ) ;
try {sleep (0 ,0);}
c a t c h ( 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 (” Interrompu ” ) ;
}
}
a f f i c h e ( ” Terminé ( ”+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 . o u t . 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 [ ]
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 . o u t . p r i n t l n ( ” Main : v a l e u r = ”+v a l ) ;
System . o u t . p r i n t l n ( ” Main : I l y a ”+n+” t h r e a d s ,
+” l a r è g l e du j e u e s t c h a q u e t h r e a d ne ” ) ;
System . o u t . p r i n t l n ( ” Main : p e u t r e t i r e r qu ’ un ”
+” nombre de p o i n t s é g a l e à s o n i d e n t i t é . \ n” )
VariablePartageeBS ( int id ) {
this . id = id ;
}
}
Arnaud Labourel, [email protected]
args ) {
Les threads en Java
VariablePartageeBienSynchronized.java
class EntierPartage
{
int valeur ;
E n t i e r P a r t a g e ( i n t v a l e u r ){
this . valeur = valeur ;
}
s y n c h r o n i z e d v o i d d e c r e m e n t e ( i n t d e c ){
v a l e u r −= d e c ;
}
s y n c h r o n i z e d b o o l e a n 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 (){
return String . valueOf ( valeur ) ;
}
}
p u b l i c void 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 ( ” A p rè s ”+v a r p a r t ) ;
try {sleep (0 ,0);}
c a t c h ( 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 (” Interrompu ” ) ;
}
}
a f f i c h e ( ” Terminé ( ”+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 . o u t . 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 [ ]
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 . o u t . p r i n t l n ( ” Main : v a l e u r = ”+v a l ) ;
System . o u t . p r i n t l n ( ” Main : I l y a ”+n+” t h r e a d s ,
+” l a r è g l e du j e u e s t c h a q u e t h r e a d ne ” ) ;
System . o u t . p r i n t l n ( ” Main : p e u t r e t i r e r qu ’ un ”
+” nombre de p o i n t s é g a l e à s o n i d e n t i t é . \ n” )
class VariablePartageeBS
e x t e n d s Thread {
static EntierPartage varpart ;
int id ;
VariablePartageeBS ( int id ) {
this . id = id ;
}
Arnaud Labourel, [email protected]
args ) {
}
Les threads en Java
Problèmes de Coexistence
L’existence de plusieurs activités au sein d’un même
environnement induit nécessairement des problèmes de
ordonnancement =⇒non-déterminisme des
exécutions
partage des données
visibilité des données
synchronisation des actions sur ces données
Arnaud Labourel, [email protected]
Les threads en Java
Conclusion Provisoire
La programmation multi-thread est délicate :
non-déterminisme,
variables partagée et synchronisation,
optimisations provoquant des comportements
parfois non intuitifs,
portabilité vs utilisation des ressources natives,
variations dans l’implémentation des machines
virtuelles,
erreurs ne se révélant que sous hautes charges (ie en
production)...
Méthodologie particulière à suivre en utilisant les
nombreuses primitives de synchronisation (apparues
depuis la version 1.5).
Arnaud Labourel, [email protected]
Les threads en Java