ThreadLocal, partie 1, principes du stockage magique

Dans l’API java, on trouve une classe au comportement tout a fait particulier, ThreadLocal. Son principe: stocker des données dans le Thread courant afin de les récupérer plus tard, dans une autre méthode par exemple, sans avoir à les passer en argument à toute la chaine d’appel. Cette classe peut vous être d’un grand secours, mais elle doit être utilisée judicieusement et surtout en extrêmement précautionneusement!

Historique

La classe ThreadLocal est apparue dans le JDK1.2, vers la fin 1998 (et oui, elle est méconnue, mais elle date!). Son utilisation principale, permettre à plusieurs threads de travailler simultanément sur une variable statique, tout en permettant à chaque Thread d’avoir sa propre instance de la variable ainsi qu’éventuellement, proposer un constructeur pour créer la valeur par défaut pour les threads qui n’en ont pas encore recu. Exemple, soit la classe suivante utilisée par 2 Threads:

public class Test { 
 //code java 5 
 public static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); 
}
Thread 1 Thread 2
valeur de x action action valeur de x
- start -
1 Test.x.set(1) -
1 start -
2 Test.x.set(Test.x.get()++) -
2 Test.x.set(5) 5
2 Test.x.get() -> 2 5
2 Test.x.set(Test.x.get()++) 6

La première implémentation (jdk 1.2) de cette classe était assez simple. Une ThreadLocal se résumait à une Map<Thread,Object>. Et le get ressemblait grosso modo à ça:

private Map map=...;
   //......
   public Object get(){
     synchronized(map){
       if (!map.contains(Thread.currentThread()))
       map.put(Thread.currentThread(),initialValue());
       return map.get(Thread.currentThread());
     }
   }

Le problème principal de ce code: ses performances. Chaque appel à la méthode get() implicant un bloc synchronized, les performances étaient assez catastrophique quand les Thread étaient nombreux. Il devait être possible d’éviter ces blocs, puisque chaque thread utilisait des données différentes.

Sun a donc revu sa copie, et a inversé la responsabilité du stockage. Au lieu d’avoir une classe ThreadLocal qui stocke les données pour chaque thread et dispatche la bonne valeur, en fonction du thread courant, on a maintenant le thread qui stocke les données, pour chaque instance des ThreadLocal de la jvm, et qui retourne la bonne valeur en fonction de l’instance de ThreadLocal utilisée.

L’avantage est évident, comme les données sont a la base stockées dans la classe Thread, il n’est plus nécessaire d’avoir de bloc synchronized, et donc on a un gain non négligeable en performances. Mais il y a un fort contrecoup à cela. Comme les données sont stockées dans le thread, c’est une grande source de memory leak (tant que le thread vis, les données y restent). C’est ce dernier schéma de stockage qui est toujours en vigueur à ce jour (java 6).

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