ThreadLocal, partie 1, principes du stockage magique

Applications pratiques

Quels sont les intérêts de la variable ThreadLocal? Personellement: je vois deux utilisation principales. La première: pouvoir attribuer à chaque thread une donnée unique. La javadoc de sun donne l’exemple suivant ((c)sun :))

import java.util.concurrent.atomic.AtomicInteger;  
 
 public class UniqueThreadIdGenerator {  
 
     private static final AtomicInteger uniqueId = new AtomicInteger(0);  
 
     private static final ThreadLocal<Integer>uniqueNum =   
         new ThreadLocal<Integer>() {  
             @Override  
             protected Integer initialValue() {  
                 return uniqueId.getAndIncrement();  
         }  
     };  
 
     public static int getCurrentThreadId() {  
         return uniqueNum.get();  
     }  
 } // UniqueThreadIdGenerator

Cette classe un peu simple permet d’avoir un identifiant unique par Thread. On y vois la rédéfinition de la méthode « initialValue() » à cette fin. Par ce biais, la valeur, pour chaque Thread, n’est calculée qu’au tout dernier moment, et le créateur du thread n’a pas à se soucier de l’initialisation de la threadlocal (il n’est peut-être même pas au courant de son existence).

Le deuxième cas d’utilisation, plus important à mon avis, c’est celui qui se pose lorsqu’on utilise plusieurs api qui n’ont pas nécessairement été prévue pour tenir compte l’une de l’autre. Imaginez une api X, dans laquelle vous appelez une méthode faireQuelqueChose(String). Cette api va faire toute une série de calculs puis, à un moment donné, appeler une méthode y(int) de votre code, au sein d’une classe qu’elle aura instanciée elle même. Vous n’avez pas le choix, c’est ainsi qu’est faite l’api en question.

Imaginez maintenant que vous ayez besoin de passer à y(int) une donnée importante à son calcul (par exemple le nom du fichier en cours de traitement). Vous pourriez stocker cette donnée dans un champ statique. Ça fonctionnerait, jusqu’au moment où l’on vous demande de faire du traitement en parallèle. Vous pourriez vous arranger pour passer cette variable en paramètre à y, qui aurait alors la signature y(int,MaClasse), mais l’ennui, c’est qu’il faudrait passer ce paramètre à toute la chaine d’appel pour arriver là. Vous n’allez quand même pas réécrire toute l’api. Vous pourriez aussi, si l’on imagine que la méthode y(int) soit implémentée sur la classe Y, créer une instance différente de Y pour chaque traitement de calcul. C’est possible (et même recommandé) si cette api vous permet de préciser l’instance de Y à utiliser. Mais si l’api en question a décidé de partager une seule et même instance de Y entre tous les threads, vous êtes chocolat. Pire encore si votre code a besoin que Y soit un singleton :)

Dans ce cas là, une nouvelle fois, on fera appel à un champ de stockage Threadlocal, et on aura un code comme suis

public class Y {  
   public static ThreadLocal<Data> data = new ThreadLocal<Data>();  
 
   public boolean y(int) {  
      Data current = data.get();  
      // traitement sur base des données préalablement stockées  
   }  
}  
 
public MaClasse {  
 
    public void duTravail(Data d){  
       FrameworkX x = ....;  
       Y.data.set(d);  
       try{  
           x.uneMethode("des données");  
       } finally {  
           Y.data.remove();  
       }  
    }  
}

C’est beau, c’est magique et ça marche du tonnerre.

Génial hein? Ben non! La suite au prochain numéro ;) Vous aurez alors enfin les explications sur le comportement de ce test. (Ben oui, vu la date j’allais pas tout vous révéler tout de suite :D)

5 réflexions au sujet de « ThreadLocal, partie 1, principes du stockage magique »

  1. Comme beaucoup de choses, ce qui compte c’est une utilisation correcte de celle-ci. Au bon endroit et en respectant certains principes, Parfois, il faut un moyen de passer des données en aval sans les passer en paramètre de méthode et soit on utilise ça, soit on réivente la roue en faisant le même à la main. Tout ça sera expliqué dans la partie deux à paraître ;)

    Mais on est d’accord sur un point je pense, a moins d’avoir une très bonne raison de le faire, à ne pas utiliser.

  2. Je me permet de faire une remarque au sujet du ThreadLocal, autant cette classe peut servir, autant je déconseille son utilisation au possible!

    En effet utiliser ce genre de chose, c’est comme si on utilisait des variables globales scopées dans une thread. Et les variables globales c’est très souvent mal!

    http://fr.wikipedia.org/wiki/Variable_globale

    Pour les les lecteurs anglophones il y a une entrée sur StackOverFlow, je conseille de lire les retours à la question : http://stackoverflow.com/questions/484635/are-global-variables-bad

Laisser un commentaire