Objet ThreadLocal
1. Introduction
Fin 1998, Sun lançait le JDK 1.2. Elle apportait alors de nombreuses nouveautés, dont celle à laquelle nous nous intéresserons ici. La classe ThreadLocal.
Cette classe se révèle très utile pour simplifier le développement de programmes multi-threads concurrent.
Pour illustrer cela, disons que vous voulez créer une application mutli-thread utilisant une même instance. En utilisant un champ statique, celui-ci pourra être utilisé dans tout les processus, mais non modifiable. En utilisant un champ non statique, celui-ci pourra être modifié dans n’importe quel processus, ce qui n’est pas forcément une bonne idée.
Pour cela, ThreadLocal peut nous simplifier la vie, en effet il nous permet de créer une variable propre à chaque Thread. L’objet ThreadLocal stocke une valeur qui n’est accessible que par le thread courant. Les autres threads n’ont pas accès à cet objet.
2. Description
2.1 Générale
Constructeur :
ThreadLocal() ;
Construit un nouvel objet ThreadLocal.
Méthodes :
protected Object initialValue()
Retourne la valeur initiale du Thread courant pour cette variable ThreadLocal.
void set(Object T)
Fixe la copie du Thread courant de cette variable ThreadLocal à la valeur indiquée.
Object get()
Retourne la valeur dans la copie du Thread courant de la variable ThreadLocal.
void remove()
Efface la valeur pour ce ThreadLocal.
Méthodes héritées de la classe java.lang.Object :
Clone(), equals(java.lang.Object), finalize(),
getClass(), hashCode(), notify(), notifyAll(),
toString(), wait(), wait(long), wait(long, int).
2.2 Details des méthodes
A défaut de méthode set(Object T) implémenter, cette méthode est appelé au plus une fois pour chaque thread créer lors du premier get().
Par défaut, initialValue() retourne simplement null, mais son comportement peut être redéfini.
Exemple :
//On redéfinie le comportement de initialValue()
class monThreadLocal extends ThreadLocal{
protected Object initialValue() {
return (String) "Je suis une valeur initiale";
// Il renvoie un chaîne de caractère
}
}
class AvecThreadLocal implements Runnable {
public monThreadLocal Tl= new monThreadLocal();
public void run() {
System.out.println(" ThreadLocal = "+Tl.get());
}
}
public class Main {
public static void main(String[] args) {
Thread T1 = new Thread(new AvecThreadLocal());
T1.start();
}
}
//sortie :
ThreadLocal = Je suis une valeur initiale
// L’objet de ThreadLocal a pris pour valeur celle que lui a renvoyer initialValue()
Cette méthode nous permet de fixer la valeur détenue dans l’objet ThreadLocal.
Celle-ci est liée au thread courant et seulement pour lui.
// On attribue nous même une valeur a l’objet contenu dans ThreadLocal par la méthode set().
class AvecThreadLocal implements Runnable {
public ThreadLocal Tl= new ThreadLocal();
public void run() {
Tl.set("Utilisateur a utilisé set()");
System.out.println(" ThreadLocal = "+Tl.get());
}
}
public class Main {
public static void main(String[] args) {
Thread T1 = new Thread(new AvecThreadLocal());
T1.start();
}
}
//sortie :
ThreadLocal = Utilisateur a utilisé set()
// L’objet de ThreadLocal a pris pour valeur celle qu’on lui a attribuée.
Cette méthode nous retourne la valeur contenue dans l’objet ThreadLocal an.
Lors du premier appelle de get(), et si set() n’a pas été invoquer précédemment par ce Thread, la méthode initialValue() est appelé afin d’établir une valeur par défaut.
Voir les exemples précédents.
Cette méthode permet d’effacer la valeur du ThreadLocal.
Si l’on désire a nouveau accéder a la valeur de ce thread, à défaut d’une méthode set(), on aura en retour un objet null, retourner par la méthode initialValue().
Exemple :
class AvecThreadLocal implements Runnable {
public ThreadLocal Tl= new ThreadLocal();
public void run() {
Tl.set("Utilisateur a utilisé set()");
System.out.println("Avant Remove: ThreadLocal = "+Tl.get());
Tl.remove();
System.out.println("Après Remove: ThreadLocal = "+Tl.get());
}
}
public class Main {
public static void main(String[] args) {
Thread T1 = new Thread(new AvecThreadLocal());
T1.start();
}
}
//Sortie:
Avant Remove: ThreadLocal = Utilisateur a utilisé set()
Après Remove: ThreadLocal = null
//La méthode remove() a supprimer le contenu de l’objet de ThreadLocal.
//Au moment de l’impression, l’appel a get() nous renvoi null, car un appel a initialValue, non
// implémenté ici, a été fait.
2.3. Remarque
• Il est aussi nécessaire de remarquer que le ThreadLocal n’existe que lorsque le Thread est vivant. Un exemple viendra illustrer cela.
3. Utilisation
Comme bien souvent, un petit exemple nous permet de bien comprendre le fonctionnement de ThreadLocal.
Exemple 1 :
class AvecThreadLocal implements Runnable { public ThreadLocal Tloc; public Object o; public AvecThreadLocal () { // Constructeur de notre AvecThreadLocal Tloc = new ThreadLocal(); o = new String (); }
public void dormir(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }
public void run() { String name = Thread.currentThread().getName(); Tloc.set("Local_"+name); // on attribut la même valeur a l’objet de ThreadLocal et o = "Objet_"+name; // à l’objet o. System.out.println("Avant attente ---NOM="+ name +"\n ThreadLocal="+Tloc.get() +" Objet="+ o); dormir(); // On met en attente le Thread courant System.out.println("Après le dodo ---NOM="+ name +"\n ThreadLocal="+Tloc.get() +" Objet="+ o); } }
public class Main {
public static void main(String[] args) { AvecThreadLocal tl = new AvecThreadLocal(); // on creer une instance AvecThreadLocal; Thread T0 = new Thread(tl); // Creation du Thread T1 Thread T1 = new Thread(tl); // T0.start(); T1.start(); try { T0.join(); T1.join(); System.out.println("\n Dans le Main: \nThreadLocal= "+tl.Tloc.get()+ " & \nObjet= " + tl.o); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Sortie : Avant attente ---NOM=Thread-0 ThreadLocal=Local_Thread-0 Objet=Objet_Thread-0 Avant attente ---NOM=Thread-1 ThreadLocal=Local_Thread-1 Objet=Objet_Thread-1 Après le dodo ---NOM=Thread-0 ThreadLocal=Local_Thread-0 Objet=Objet_Thread-1 Après le dodo ---NOM=Thread-1 ThreadLocal=Local_Thread-1 Objet=Objet_Thread-1
Dans le Main: ThreadLocal= null & Objet= Objet_Thread-1
Remarqué bien ce que nous renvoie ce programme. La valeur de l’objet de ThreadLocal , contrairement a l’objet « simple », n’a pas été modifier pendant l’attente. En effet, le ThreadLocal est exclusif au Thread courant. Contrairement aux autres objet, ou tout les Threads se le partage, et y ont donc accès.
De plus, après exécution des Threads, la valeur du ThreadLocal est null. Ceci est dû au fait que le ThreadLocal n’existe que pendant qu’ils sont actifs.
4. Conclusion
Comme nous venons de le voir, cette classe peut être d’une utilité certaine. Elle simplifie la programmation au niveau des threads, nous permettant d’utiliser à la fois des objets utilisables par tout les threads, et des objets exclusif au thread courant grâce a ThreadLocal.
A vous de jouer.
|