Dessiner deux têtes

     

L'objectif est de démontrer l'utilité de l'indication synchronized devant une méthode ou un bloc d'instruction.

alt : l'applet n'est pas visible par votre navigateur ; pour voir l'application, vous devez télécharger le code, le compiler et l'exécuter

Appuyer sur le bouton dessine ou appuyer sur les bouton dessineS provoque le démarrage de deux threads identiques (à un paramètre de couleur près) ayant pour objectif le dessin d'une tête. Les deux threads partagent un ensemble de données : les positions successives x et y des ovales à tracer, le JPanel dans laquelle il faut dessiner et la couleur du dessin. Entre les actions consécutives à un clic sur dessine et dessineS, il y a une seule différence : la méthode run utilisée lorsqu'on actionne dessineS est déclarée synchronized, tandis que la méthode run utilisée lorsqu'on actionne dessine n'est pas synchronisée.

Un peu de cours d'introduction à la synchronisation

Lorsqu'une application est multithread, les différents threads se partagent certaines données de l'application. Il peut alors être nécessaire d'effectuer une synchronisation entre les différents threads afin de conserver une cohérence sur les données qu'ils partagent.

Tout objet ou tableau est muni d'un verrou qui peut être « ouvert » ou « fermé » ; nous ne parlerons ci-dessous que de verrous sur des objets, mais tout ce qui sera dit pourra aussi s'appliquer à des verrous sur des tableaux. Ce sont ces verrous qui vont nous permettre de synchroniser des threads. Un thread peut fermer le verrou d'un objet (à condition que ce verrou ne soit pas déjà fermé) et il est alors le seul à pouvoir rouvrir le verrou.

Supposons qu'un premier thread t1 puisse exécuter un bloc d'instructions (ou une méthode) noté b1, qu'un second thread t2 puisse exécuter un bloc d'instructions (ou une méthode) noté b2 et enfin qu'on souhaite que les exécutions de b1 et de b2 ne puissent pas s'entremêler ; il faut alors synchroniser b1 et b2 ; un procédé astucieux a été mis en place pour traiter ce problème. On décide que t1 doit fermer le verrou d'un objet obj pour pouvoir exécuter b1 et qu'il en est de même pour t2 avec b2, et sur le même objet obj. Lorsque t1 doit exécuter b1 :

Le second thread procède de même. On dit que b1 et b2 sont synchronisés sur obj. Plusieurs threads peuvent attendre simultanément le déverrouillage d'un même objet. Quand le verrou sera rouvert, un seul d'entre eux pourra le fermer.

Si des méthodes doivent se synchroniser, c'est le plus souvent pour ne pas intervenir simultanément sur un même objet ; dans ce cas, l'objet obj sur lequel on effectue la synchronisation est en général cet objet, c'est le plus naturel.

On peut synchroniser une méthode meth sur l'objet représenté par this de la manière suivante :

synchronized meth() {
	...
}
On dit alors simplement que meth est une méthode synchronisée. La méthode est synchronisée sur l'objet sur lequel elle est invoquée ; si on invoque obj.meth(), la méthode est synchronisée sur obj.

Pour synchroniser un bloc d'instructions sur un objet obj, on procède comme ci-dessous :

synchronized(obj) {
	...
}
On peut noter que le verrouillage d'un objet obj n'empêche pas une méthode non synchronisée sur obj de venir éventuellement modifier les attributs de obj.

Lorsqu'une classe redéfinit une méthode synchronisée de sa superclasse, la méthode redéfinie peut, selon le choix du programmeur, être synchronisée ou non.

Signalons qu'une méthode statique peut aussi être déclarée synchronized. La règle correspondante est que deux méthodes statiques d'une même classe ne peuvent être invoquées simultanément. En revanche, le fait qu'une méthode statique verrouille une classe ne pose aucun verou sur les objets d'instance de la classe et n'empêche pas d'utiliser des méthodes d'instance de la classe.

Nous ne commentons pas le code de l'application représentée par l'applet ci-dessus. Nous vous conseillons de consulter les exemples suivants qui sont plus simples.

Vous pouvez voir les fichiers :

.

Le fichier ExecutantSynchroBis.java est équivalent à ExecutantSynchro.java, mais en utilisant l'instruction synchronized au moment de l'utilisation de la méthode run au lieu de faire de une méthode run la méthode synchronized.


© Irène Charon, Télécom ParisTech 2011