Question Quand et comment dois-je utiliser une variable ThreadLocal?


Quand devrais-je utiliser un ThreadLocal variable?

Comment est-ce utilisé?


780
2018-05-03 19:59


origine


Réponses:


Une utilisation possible (et courante) est quand vous avez un objet qui n'est pas sûr pour les threads, mais que vous voulez éviter synchroniser l'accès à cet objet (je te regarde, SimpleDateFormat). Au lieu de cela, donnez à chaque thread sa propre instance de l'objet.

Par exemple:

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyyMMdd HHmm");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

Documentation.


777
2018-05-03 20:26



Depuis un ThreadLocal est une référence aux données dans un donné Thread, vous pouvez vous retrouver avec des fuites de classloading lors de l'utilisation ThreadLocals dans les serveurs d'applications qui utilisent des pools de threads. Vous devez faire très attention à nettoyer tout ThreadLocals vous get() ou set() en utilisant le ThreadLocalde remove() méthode.

Si vous ne nettoyez pas lorsque vous avez terminé, les références qu'il contient aux classes chargées dans le cadre d'une application Web déployée resteront dans le tas permanent et ne sera jamais ramassé les ordures. Redéployer / annuler le déploiement de la webapp ne nettoiera pas chaque Threadréférence à la classe (s) de votre webapp depuis le Thread n'est pas quelque chose qui appartient à votre webapp. Chaque déploiement successif créera une nouvelle instance de la classe qui ne sera jamais récupérée.

Vous allez vous retrouver avec des exceptions de mémoire insuffisante dues à java.lang.OutOfMemoryError: PermGen space et après quelque googling va probablement augmenter -XX:MaxPermSize au lieu de réparer le bug.

Si vous rencontrez ces problèmes, vous pouvez déterminer quel thread et quelle classe conservent ces références en utilisant L'analyseur de mémoire d'Eclipse et / ou en suivant Le guide de Frank Kieviet et suivre.

Mise à jour: redécouverte Entrée du blog d'Alex Vasseur cela m'a aidé à retrouver quelques ThreadLocal problèmes que j'avais.


380
2018-05-03 22:02



De nombreux frameworks utilisent ThreadLocals pour conserver un contexte lié au thread en cours. Par exemple, lorsque la transaction en cours est stockée dans un ThreadLocal, vous n'avez pas besoin de la passer en paramètre à chaque appel de méthode, au cas où quelqu'un dans la pile aurait besoin d'y accéder. Les applications Web peuvent stocker des informations sur la requête et la session en cours dans un ThreadLocal, afin que l'application puisse y accéder facilement. Avec Guice, vous pouvez utiliser ThreadLocals lors de la mise en œuvre étendues personnalisées pour les objets injectés (défaut de Guice étendues de servlet très probablement les utiliser aussi bien).

Les ThreadLocals sont une sorte de variables globales (bien qu'elles soient légèrement moins mauvaises parce qu'elles sont limitées à un thread), vous devriez donc faire attention en les utilisant pour éviter les effets secondaires indésirables et les fuites de mémoire. Concevez vos API afin que les valeurs ThreadLocal soient toujours automatiquement effacées lorsqu'elles ne sont plus nécessaires et qu'une utilisation incorrecte de l'API ne sera pas possible (par exemple comme ça). ThreadLocals peut être utilisé pour rendre le code plus propre, et dans de rares cas, ils sont la seule façon de faire fonctionner quelque chose (mon projet actuel avait deux cas de ce type, ils sont documentés ici sous "Champs statiques et variables globales").


123
2018-05-04 13:36



En Java, si vous avez une donnée qui peut varier par thread, vous avez le choix de passer cette donnée à toutes les méthodes qui en ont besoin (ou peuvent avoir besoin) ou d'associer la donnée au thread. Passer le datum partout peut être réalisable si toutes vos méthodes doivent déjà passer autour d'une variable "contexte" commune.

Si ce n'est pas le cas, vous pouvez ne pas vouloir encombrer vos signatures de méthode avec un paramètre supplémentaire. Dans un monde non-threaded, vous pouvez résoudre le problème avec l'équivalent Java d'une variable globale. Dans un mot threadé, l'équivalent d'une variable globale est une variable locale-thread.


42
2018-05-03 20:20



Essentiellement, quand vous avez besoin d'un La valeur de la variable dépend du thread actuel et cela n'est pas pratique pour vous d'attacher la valeur au fil d'une autre manière (par exemple, sous-classe thread).

Un cas typique est où un autre cadre a créé le fil que votre code s'exécute, par ex. un conteneur de servlet, ou là où il est plus logique d'utiliser ThreadLocal parce que votre variable est alors "à sa place logique" (plutôt qu'une variable suspendue à une sous-classe Thread ou à une autre table de hachage).

Sur mon site web, j'ai encore d'autres discussion et exemples d'utilisation de ThreadLocal cela peut aussi être intéressant.

Certaines personnes préconisent l'utilisation de ThreadLocal comme un moyen d'attacher un "thread ID" à chaque thread dans certains algorithmes concurrents où vous avez besoin d'un numéro de thread (voir par exemple Herlihy & Shavit). Dans ce cas, vérifiez que vous obtenez vraiment un avantage!


13
2018-05-03 23:50



Il y a un très bon exemple en livre Java Concurrency en pratique. Où auteur (Joshua Bloch) explique comment confinement du fil est l'un des moyens les plus simples pour atteindre la sécurité du fil et ThreadLocal est un moyen plus formel de maintenir le confinement du fil. À la fin, il explique également comment les gens peuvent en abuser en l'utilisant comme variables globales.

J'ai copié le texte du livre mentionné mais le code 3.10 est manquant car il n'est pas très important de comprendre où ThreadLocal devrait être utilisé.

Les variables thread-locales sont souvent utilisées pour empêcher le partage dans les conceptions basées sur des singletons mutables ou des variables globales. Par exemple, une application monothread peut maintenir une connexion de base de données globale initialisée au démarrage pour éviter d'avoir à passer une connexion à chaque méthode. Étant donné que les connexions JDBC peuvent ne pas être thread-safe, une application multithread qui utilise une connexion globale sans coordination supplémentaire n'est pas non plus sécurisée pour les threads. En utilisant un ThreadLocal pour stocker la connexion JDBC, comme dans ConnectionHolder dans la liste 3.10, chaque thread aura sa propre connexion.

ThreadLocal est largement utilisé dans la mise en œuvre des frameworks d'application. Par exemple, les conteneurs J2EE associent un contexte de transaction à un thread en cours d'exécution pour la durée d'un appel EJB. Ceci est facilement implémenté en utilisant un Thread-Local statique contenant le contexte de la transaction: lorsque le code du framework a besoin de déterminer quelle transaction est en cours d'exécution, il récupère le contexte de la transaction à partir de ce ThreadLocal. Cela est pratique en ce sens qu'il réduit le besoin de transmettre des informations de contexte d'exécution dans chaque méthode, mais couple tout code qui utilise ce mécanisme à la structure.

Il est facile d'abuser de ThreadLocal en traitant sa propriété de confinement de threads comme une licence permettant d'utiliser des variables globales ou comme un moyen de créer des arguments de méthode "cachés". Comme les variables globales, les variables locales-thread peuvent nuire à la réutilisabilité et introduire des couplages cachés entre les classes, et doivent donc être utilisées avec précaution.


11
2017-10-04 09:28



La documentation le dit très bien: "chaque thread qui accède à [une variable locale-thread] (via sa méthode get ou set) a sa propre copie initialisée indépendamment de la variable".

Vous en utilisez un quand chaque thread doit avoir sa propre copie de quelque chose. Par défaut, les données sont partagées entre les threads.


9
2018-05-03 20:05