Quand et comment dois-je utiliser une variable ThreadLocal?


877

Quand dois-je utiliser une ThreadLocalvariable?

Comment est-ce utilisé?


11
Si vous utilisez ThreadLocal, n'écrivez jamais un wrapper dessus !!! Chaque développeur qui veut utiliser la variable doit savoir que c'est «ThreadLocal»
Pas un bug

2
@Notabug Si vous êtes explicite, vous pouvez nommer votre variable afin que ce soit vous qui ayez affaire à une valeur ThreadLocal, par exemple RequestUtils.getCurrentRequestThreadLocal(). Cela ne veut pas dire que c'est très élégant, mais cela vient du fait que ThreadLocal lui-même n'est pas très élégant dans la plupart des cas.
whirlwin

@Sasha Le ThreadLocal peut-il être utilisé pour stocker la trace d'exécution
gaurav

Réponses:


864

Une utilisation possible (et courante) est lorsque vous avez un objet qui n'est pas thread-safe, mais que vous voulez éviter de synchroniser l' accès à cet objet (je vous 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 .


160
Une autre alternative à la synchronisation ou au threadlocal est de faire de la variable une variable locale. Les vars locaux sont toujours thread-safe. Je suppose que c'est une mauvaise pratique de rendre DateFormats local car ils sont chers à créer, mais je n'ai jamais vu de métriques solides sur ce sujet.
Julien Chastang

18
C'est un prix élevé à payer pour le piratage SimpleDateFormat. Il est peut-être préférable d'utiliser une alternative thread-safe . Si vous êtes d' accord que les singletons sont mauvais alors ThreadLocalest encore pire.
Alexander Ryzhov

4
@overthink Y a-t-il une raison pour déclarer ThreadLocal statique et final, je veux dire performance ou quelque chose?
Sachin Gorade

4
La méthode ThreadLocal.get () appellera ThreadLocal.initialValue () (une fois) pour chaque thread, ce qui signifie qu'un objet SimpleDateFormat est créé pour chaque thread. N'est-il pas préférable d'avoir simplement SimpleDateFormat comme variables locales (puisque nous n'avons pas à traiter les problèmes de récupération de place)?
sudeepdino008

3
Faites juste attention à ne pas utiliser l'initialisation à double accolade pour votre SimpleDateFormat car cela créerait une classe anonyme de sorte que votre chargeur de classe ne pourrait pas être récupéré. fuite de mémoire: renvoyer le nouveau SimpleDateFormat () {{applyPattern ("yyyyMMdd HHmm")}};
user1944408

427

Étant donné que a ThreadLocalest une référence aux données dans une donnée Thread, vous pouvez vous retrouver avec des fuites de chargement de classe lorsque vous utilisez ThreadLocals dans des serveurs d'applications utilisant des pools de threads. Vous devez être très prudent de nettoyer tout ThreadLocalde vous get()ou set()en utilisant le ThreadLocalde remove()méthode.

Si vous ne nettoyez pas lorsque vous avez terminé, toutes les références qu'il contient aux classes chargées dans le cadre d'une webapp déployée resteront dans le tas permanent et ne seront jamais récupérées. Redéployer / annuler le déploiement de la webapp ne nettoiera pas Threadla référence de chacun aux classes de votre webapp car ce Threadn'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 vous retrouverez avec des exceptions de mémoire insuffisante en raison de java.lang.OutOfMemoryError: PermGen spaceet après quelques recherches sur Google, cela augmentera probablement au -XX:MaxPermSizelieu de corriger le bogue.

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 et le suivi de Frank Kieviet .

Mise à jour: redécouvert l'entrée de blog d'Alex Vasseur qui m'a aidé à retrouver certains ThreadLocalproblèmes que j'avais.


11
Alex Vasseur a déplacé son blog. Voici un lien actuel vers l'article sur les fuites de mémoire.
Kenster

12
Il semble que le fil vers lequel Julien se connecte soit passé ici , et vaut bien la peine d'être lu…
Donal Fellows

28
C'est énormément de votes positifs pour une réponse qui, bien qu'informative, ne répond en rien à la question.
Robin

8
Maintenant que PermGen est tué par Java 8, cela modifie-t-il cette réponse de quelque manière que ce soit?
Evil Washing Machine

13
@Robin: Je ne suis pas d'accord. La question est de savoir comment utiliser correctement ThreadLocal, pour avoir une compréhension complète de tout concept (ThreadLocal ici), il est également important de comprendre comment ne pas l'utiliser et les risques de l'utiliser négligemment, et c'est ce que la réponse de Phil est à propos. Je suis content qu'il ait couvert ce point qui n'est couvert dans aucune autre réponse. Tous ces votes sont bien mérités. Je crois que SO devrait développer la compréhension des concepts plutôt que d'être simplement un site d'AQ.
Saurabh Patil,

166

De nombreux frameworks utilisent ThreadLocals pour maintenir un certain contexte lié au thread actuel. Par exemple, lorsque la transaction en cours est stockée dans un ThreadLocal, vous n'avez pas besoin de la transmettre en tant que paramètre à chaque appel de méthode, au cas où quelqu'un en bas de la pile aurait besoin d'y accéder. Les applications Web peuvent stocker des informations sur la demande 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 l'implémentation de portées personnalisées pour les objets injectés (les portées de servlet par défaut de Guice les utilisent très probablement également).

Les ThreadLocals sont une sorte de variables globales (bien que légèrement moins mauvaises car elles sont limitées à un seul thread), vous devez donc être prudent lorsque vous les utilisez pour éviter les effets secondaires indésirables et les fuites de mémoire. Concevez vos API de sorte 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 ceci ). ThreadLocals peut être utilisé pour rendre le code plus propre, et dans de rares cas, ils sont le seul moyen de faire fonctionner quelque chose (mon projet actuel avait deux de ces cas; ils sont documentés ici sous "Champs statiques et variables globales").


4
C'est exactement comme cela que le framework exPOJO (www.expojo.com) permet d'accéder à ORM Session / PersistenceManager sans avoir besoin de la surcharge d'annotations et d'injection. C'est un peu comme «injection de thread» au lieu de «injection d'objet». Il fournit un accès aux dépendances sans l'exigence (et la surcharge) de les avoir incorporées dans chaque objet qui pourrait avoir besoin de ces dépendances. C'est incroyable de voir comment 'léger' vous pouvez créer un cadre DI lorsque vous utilisez l'injection de fil au lieu du DI classique (par exemple, Spring etc.,)
Volksman

27
Pourquoi ai-je dû faire défiler la liste jusqu'à présent pour trouver cette réponse essentielle!?
Jeremy Stein

1
@Esko, Dans votre projet, votre utilisation de ThreadLocal dans hashcode et equals est inutile. Une meilleure approche consiste à créer ou à transmettre une référence à une fonction hashcode / equals chaque fois que cela est nécessaire. De cette façon, vous pouvez éviter complètement le besoin de pirater ThreadLocal.
Pacerier


1
C'est la meilleure explication de l'utilisation de TL.
Dexter

52

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

Si ce n'est pas le cas, vous ne voudrez peut-être pas encombrer vos signatures de méthode avec un paramètre supplémentaire. Dans un monde sans thread, 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 de thread.


13
Vous devez donc éviter les sections locales de thread de la même manière que vous évitez les globales. Je ne peux pas accepter qu'il soit OK de créer des globaux (sections locales de threads) au lieu de transmettre des valeurs, les gens n'aiment pas, car cela révèle souvent des problèmes d'architecture qu'ils ne veulent pas résoudre.
Juan Mendes

10
Peut-être ... Cela peut être utile, cependant, si vous avez une énorme base de code existante à laquelle vous devez ajouter une nouvelle donnée qui doit être transmise partout , par exemple un contexte de session, une transaction db, un utilisateur connecté, etc. .
Cornel Masson

6
Félicitations pour l'utilisation de données au lieu de données.
profond

Cela m'a rendu curieux. 'datum' is the singular form and 'data' is the plural form.
Siddhartha

23

Il y a un très bon exemple dans le livre Java Concurrency in Practice . Où l'auteur ( Joshua Bloch ) explique comment le confinement de threads est l'un des moyens les plus simples pour garantir la sécurité des threads et ThreadLocal est un moyen plus formel de maintenir le confinement des threads. À 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 doit être utilisé.

Les variables locales de thread 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 à thread unique peut maintenir une connexion de base de données globale qui est initialisée au démarrage pour éviter d'avoir à transmettre 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 thread-safe. En utilisant un ThreadLocal pour stocker la connexion JDBC, comme dans ConnectionHolder du Listing 3.10, chaque thread aura sa propre connexion.

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

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


18

Essentiellement, lorsque vous avez besoin que la valeur d' une variable dépende du thread actuel et qu'il ne vous soit pas pratique d'attacher la valeur au thread d'une autre manière (par exemple, sous-classer le thread).

Un cas typique est celui où un autre framework a créé le thread lequel votre code s'exécute, par exemple un conteneur de servlet, ou où il est plus logique d'utiliser ThreadLocal car votre variable est alors "à sa place logique" (plutôt qu'une variable suspendu à une sous-classe Thread ou dans une autre carte de hachage).

Sur mon site Web, j'ai d'autres discussions et des exemples d'utilisation de ThreadLocal qui peuvent également être intéressants.

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


Le ThreadLocal peut-il être utilisé pour stocker la trace d'exécution
gaurav

13
  1. ThreadLocal en Java avait été introduit sur JDK 1.2 mais a été plus tard généré dans JDK 1.5 pour introduire la sécurité de type sur la variable ThreadLocal.

  2. ThreadLocal peut être associé à la portée Thread, tout le code qui est exécuté par Thread a accès aux variables ThreadLocal mais deux threads ne peuvent pas se voir la variable ThreadLocal.

  3. Chaque thread contient une copie exclusive de la variable ThreadLocal qui devient éligible à la collecte des ordures une fois le thread terminé ou mort, normalement ou en raison d'une exception, étant donné que la variable ThreadLocal n'a pas d'autres références actives.

  4. Les variables ThreadLocal en Java sont généralement des champs statiques privés dans Classes et conservent leur état dans Thread.

En savoir plus: ThreadLocal en Java - Exemple de programme et tutoriel


Le ThreadLocal peut-il être utilisé pour stocker la trace d'exécution
gaurav

11

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

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


19
Par défaut, les objets statiques, ou les objets passés explicitement entre les threads où les deux threads possèdent une référence au même objet, sont partagés. Les objets déclarés localement dans un thread ne sont pas partagés (ils sont locaux dans la pile du thread). Je voulais juste clarifier cela.
Ian Varley

@IanVarley: Merci de l'avoir signalé. Donc, si nous avons une variable qui est déclarée et utilisée dans la classe MyThread, alors avons-nous besoin de ThreadLocal dans ce cas? Exemple: la classe MyThread étend Thread {String var1 ;, int var2; } dans ce cas, var1 et var2 feront partie de la pile de Thread et ne sont pas partagés, alors avons-nous besoin de ThreadLocal dans ce cas? Je peux me tromper totalement dans ma compréhension, veuillez guider.
Jayesh

4
Si chaque thread veut avoir sa propre copie de quelque chose, pourquoi ne peut-il pas simplement être déclaré local (qui est toujours thread-safe)?
sudeepdino008

@ sudeepdino008 vous n'avez pas toujours le contrôle de la création de ces threads (frameworks webapp, bibliothèques de pools de threads)
JMess

10

Le serveur Webapp peut conserver un pool de threads, et un ThreadLocalvar doit être supprimé avant la réponse au client, donc le thread actuel peut être réutilisé par la prochaine demande.


8

Deux cas d'utilisation où la variable threadlocal peut être utilisée -
1- Lorsque nous avons besoin d'associer un état à un thread (par exemple, un ID utilisateur ou un ID de transaction). Cela se produit généralement avec une application Web selon laquelle chaque requête envoyée à un servlet est associée à un ID de transaction unique.

// This class will provide a thread local variable which
// will provide a unique ID for each thread
class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);

    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId =
        ThreadLocal.<Integer>withInitial(()-> {return nextId.getAndIncrement();});

    // Returns the current thread's unique ID, assigning it if necessary
    public static int get() {
        return threadId.get();
    }
}

Notez qu'ici la méthode withInitial est implémentée en utilisant l'expression lambda.
2- Un autre cas d'utilisation est lorsque nous voulons avoir une instance thread-safe et que nous ne voulons pas utiliser la synchronisation car le coût de performance avec la synchronisation est plus élevé. Un tel cas est lorsque SimpleDateFormat est utilisé. Puisque SimpleDateFormat n'est pas sûr pour les threads, nous devons donc fournir un mécanisme pour le rendre sûr pour les threads.

public class ThreadLocalDemo1 implements Runnable {
    // threadlocal variable is created
    private static final ThreadLocal<SimpleDateFormat> dateFormat = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue(){
            System.out.println("Initializing SimpleDateFormat for - " + Thread.currentThread().getName() );
            return new SimpleDateFormat("dd/MM/yyyy");
        }
    };

    public static void main(String[] args) {
        ThreadLocalDemo1 td = new ThreadLocalDemo1();
        // Two threads are created
        Thread t1 = new Thread(td, "Thread-1");
        Thread t2 = new Thread(td, "Thread-2");
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
        System.out.println("Thread run execution started for " + Thread.currentThread().getName());
        System.out.println("Date formatter pattern is  " + dateFormat.get().toPattern());
        System.out.println("Formatted date is " + dateFormat.get().format(new Date()));
    } 

}

Je ne voyais toujours pas l'avantage d'utiliser ThreadLocal pour générer l'ID unique dans l'exemple un si tout votre but est de renvoyer une valeur entière unique incrémentielle. `` `public class ThreadId {// Entier atomique contenant l'ID de thread suivant à attribuer à un statique final final AtomicInteger nextId = new AtomicInteger (0); // Renvoie l'ID unique du thread actuel, en l'affectant si nécessaire public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen

7

Depuis la version Java 8, il existe un moyen plus déclaratif d'initialiser ThreadLocal:

ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");

Jusqu'à la sortie de Java 8, vous deviez effectuer les opérations suivantes:

ThreadLocal<String> local = new ThreadLocal<String>(){
    @Override
    protected String initialValue() {
        return "init value";
    }
};

De plus, si la méthode d'instanciation (constructeur, méthode d'usine) de la classe utilisée pour ThreadLocalne prend aucun paramètre, vous pouvez simplement utiliser des références de méthode (introduites dans Java 8):

class NotThreadSafe {
    // no parameters
    public NotThreadSafe(){}
}

ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);

Remarque: l' évaluation est paresseuse car vous passez java.util.function.Supplierlambda qui n'est évalué que lorsque ThreadLocal#getest appelé mais la valeur n'a pas été évaluée auparavant.


5

Vous devez être très prudent avec le modèle ThreadLocal. Il y a des inconvénients majeurs comme Phil l'a mentionné, mais celui qui n'a pas été mentionné est de s'assurer que le code qui configure le contexte ThreadLocal n'est pas "rentrant".

De mauvaises choses peuvent se produire lorsque le code qui définit les informations est exécuté une deuxième ou une troisième fois car les informations sur votre thread peuvent commencer à muter lorsque vous ne vous y attendiez pas. Veillez donc à vous assurer que les informations ThreadLocal n'ont pas été définies avant de les redéfinir.


2
La rentrée n'est pas un problème si le code est prêt à y faire face. À l'entrée, notez si la variable est déjà définie et à la sortie, restaurez sa valeur précédente (le cas échéant) ou supprimez-la (sinon).
supercat

1
@Jeff, Mec, c'est vrai pour chaque code que vous écrivez, pas seulement pour les ThreadLocalmodèles. Si vous le faites F(){ member=random(); F2(); write(member); }et que F2 remplace le membre avec une nouvelle valeur, alors évidemment, write(member)il n'écrira plus le nombre que vous avez random()édité. C'est littéralement juste du bon sens. De même, si vous le faites F(){ F(); }, alors bonne chance avec votre boucle infinie! Cela est vrai partout et n'est pas spécifique à ThreadLocal.
Pacerier

@Jeff, exemple de code?
Pacerier

5

quand?

Lorsqu'un objet n'est pas thread-safe, au lieu de la synchronisation qui entrave l'évolutivité, donnez un objet à chaque thread et conservez-lui la portée du thread, qui est ThreadLocal. La connexion à la base de données et JMSConnection sont l'un des objets les plus souvent utilisés mais pas thread-safe.

Comment ?

Un exemple est le framework Spring qui utilise fortement ThreadLocal pour gérer les transactions en arrière-plan en conservant ces objets de connexion dans les variables ThreadLocal. À un niveau élevé, lorsqu'une transaction est lancée, elle obtient la connexion (et désactive la validation automatique) et la conserve dans ThreadLocal. lors d'autres appels db, il utilise la même connexion pour communiquer avec db. À la fin, il prend la connexion de ThreadLocal et valide (ou annule) la transaction et libère la connexion.

Je pense que log4j utilise également ThreadLocal pour maintenir MDC.


5

ThreadLocal est utile lorsque vous souhaitez avoir un état qui ne doit pas être partagé entre différents threads, mais qui doit être accessible à partir de chaque thread pendant toute sa durée de vie.

Par exemple, imaginez une application Web, où chaque demande est servie par un thread différent. Imaginez que pour chaque demande, vous ayez besoin d'un élément de données plusieurs fois, ce qui est assez coûteux à calculer. Cependant, ces données peuvent avoir changé pour chaque demande entrante, ce qui signifie que vous ne pouvez pas utiliser un cache simple. Une solution simple et rapide à ce problème serait d'avoir une ThreadLocalvariable détenant l'accès à ces données, de sorte que vous ne devez la calculer qu'une seule fois pour chaque demande. Bien sûr, ce problème peut également être résolu sans utiliser deThreadLocal , mais je l'ai conçu à des fins d'illustration.

Cela dit, gardez à l'esprit que les ThreadLocals sont essentiellement une forme d'État mondial. En conséquence, il a de nombreuses autres implications et ne doit être utilisé qu'après avoir considéré toutes les autres solutions possibles.


ThreadLocals ne sont PAS un état global, sauf si vous en faites un état global. Ce sont des ressources de pile pratiquement toujours accessibles. Vous pouvez simuler un thread local en passant simplement cette variable dans toutes vos méthodes (ce qui est retardé); cela ne fait pas de cet état mondial ...
Enerccio

C'est une forme d'état global, dans le sens où il est accessible de n'importe où dans le code (dans le même contexte de thread). Cela s'accompagne de toutes les répercussions, comme le fait de ne pas pouvoir déterminer qui lit et écrit cette valeur. L'utilisation d'un paramètre de fonction n'est pas une chose retardée, c'est une bonne pratique de promouvoir des interfaces propres. Cependant, je suis d'accord avec vous que passer un paramètre sur toute la profondeur de votre base de code est une odeur de code. Mais, je crois aussi que dans de nombreuses occasions, l'utilisation de ThreadLocal est une odeur de code en premier lieu qui vous a conduit ici, c'est donc ce que l'on devrait reconsidérer.
Dimos

Il peut simplement faire partie de l'état de l'objet et ne doit pas être utilisé globalement. Bien sûr, vous obtenez un peu de surcharge, mais si plusieurs objets doivent avoir un état différent par thread, vous pouvez utiliser ThreadLocalcomme champ d'objet ...
Enerccio

Tu as raison. Je suis probablement tombé sur plusieurs utilisations abusives de ThreadLocal, où il était accessible à l'échelle mondiale. Comme vous l'avez dit, il peut toujours être utilisé comme champ de classe limitant la visibilité.
Dimos

4

Rien de vraiment nouveau ici, mais j'ai découvert aujourd'hui que ThreadLocalc'est très utile lors de l'utilisation de Bean Validation dans une application web. Les messages de validation sont localisés, mais par défaut, ils sont utilisés Locale.getDefault(). Vous pouvez configurer le Validatoravec un autre MessageInterpolator, mais il n'y a aucun moyen de spécifier le Localelorsque vous appelez validate. Ainsi, vous pouvez créer un statique ThreadLocal<Locale>(ou mieux encore, un conteneur général avec d'autres éléments dont vous pourriez avoir besoin ThreadLocal, puis demander à votre MessageInterpolatorchoix de le choisir Locale. L'étape suivante consiste à écrire un ServletFilterqui utilise une valeur de session ou request.getLocale()à choisir les paramètres régionaux et à stocker dans votre ThreadLocalréférence.


4

Comme cela a été mentionné par @unknown (google), son utilisation est de définir une variable globale dans laquelle la valeur référencée peut être unique dans chaque thread. Ses utilisations impliquent généralement le stockage d'une sorte d'informations contextuelles liées au thread d'exécution actuel.

Nous l'utilisons dans un environnement Java EE pour transmettre l'identité de l'utilisateur à des classes qui ne sont pas compatibles avec Java EE (n'ont pas accès à HttpSession ou à l'EJB SessionContext). De cette façon, le code, qui utilise l'identité pour les opérations basées sur la sécurité, peut accéder à l'identité de n'importe où, sans avoir à la transmettre explicitement dans chaque appel de méthode.

Le cycle d'opérations de demande / réponse dans la plupart des appels Java EE facilite ce type d'utilisation car il fournit des points d'entrée et de sortie bien définis pour définir et désactiver le ThreadLocal.


4

ThreadLocal garantira que l'accès à l'objet mutable par les multiples threads de la méthode non synchronisée est synchronisé, ce qui signifie que l'objet mutable est immuable dans la méthode.

Ceci est réalisé en donnant une nouvelle instance d'objet mutable pour chaque thread essayez d'y accéder. Il s'agit donc d'une copie locale sur chaque thread. Il s'agit d'un hack sur la création d'une variable d'instance dans une méthode accessible comme une variable locale. Comme vous savez que la variable locale de méthode n'est disponible que pour le thread, une différence est; les variables locales de méthode ne seront pas disponibles pour le thread une fois l'exécution de la méthode terminée, car un objet mutable partagé avec threadlocal sera disponible sur plusieurs méthodes jusqu'à ce que nous le nettoyions.

Par définition:

La classe ThreadLocal en Java vous permet de créer des variables qui ne peuvent être lues et écrites que par le même thread. Ainsi, même si deux threads exécutent le même code et que le code a une référence à une variable ThreadLocal, les deux threads ne peuvent pas voir les variables ThreadLocal de l'autre.

Chacun Threaden java en contient ThreadLocalMap.

Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.

Atteindre le ThreadLocal:

Créez maintenant une classe wrapper pour ThreadLocal qui va contenir l'objet mutable comme ci-dessous (avec ou sans initialValue()).
Maintenant, getter et setter de ce wrapper fonctionneront sur une instance threadlocal au lieu d'un objet mutable.

Si getter () de threadlocal n'a trouvé aucune valeur avec dans le threadlocalmap du Thread; il invoquera ensuite initialValue () pour obtenir sa copie privée par rapport au thread.

class SimpleDateFormatInstancePerThread {

    private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd") {
                UUID id = UUID.randomUUID();
                @Override
                public String toString() {
                    return id.toString();
                };
            };
            System.out.println("Creating SimpleDateFormat instance " + dateFormat +" for Thread : " + Thread.currentThread().getName());
            return dateFormat;
        }
    };

    /*
     * Every time there is a call for DateFormat, ThreadLocal will return calling
     * Thread's copy of SimpleDateFormat
     */
    public static DateFormat getDateFormatter() {
        return dateFormatHolder.get();
    }

    public static void cleanup() {
        dateFormatHolder.remove();
    }
}

Maintenant wrapper.getDateFormatter(), appellera threadlocal.get()et vérifiera que currentThread.threadLocalMapcontient cette instance (threadlocal).
Si oui, retournez la valeur (SimpleDateFormat) pour l'instance threadlocal correspondante,
sinon ajoutez la carte avec cette instance threadlocal, initialValue ().

Ci-joint la sécurité du fil atteint sur cette classe mutable; par chaque thread travaille avec sa propre instance mutable mais avec la même instance ThreadLocal. Signifie que tout le thread partagera la même instance ThreadLocal comme clé, mais une instance SimpleDateFormat différente comme valeur.

https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java


3

Les variables locales de thread sont souvent utilisées pour empêcher le partage dans les conceptions basées sur des singletons mutables ou des variables globales.

Il peut être utilisé dans des scénarios tels que l'établissement d'une connexion JDBC distincte pour chaque thread lorsque vous n'utilisez pas de pool de connexions.

private static ThreadLocal<Connection> connectionHolder
           = new ThreadLocal<Connection>() {
      public Connection initialValue() {
           return DriverManager.getConnection(DB_URL);
          }
     };

public static Connection getConnection() {
      return connectionHolder.get();
} 

Lorsque vous appelez getConnection, il renvoie une connexion associée à ce thread. La même chose peut être faite avec d'autres propriétés comme dateformat, contexte de transaction que vous ne souhaitez pas partager entre les threads.

Vous pourriez également avoir utilisé des variables locales pour la même chose, mais ces ressources prennent généralement du temps dans la création, vous ne voulez donc pas les créer encore et encore chaque fois que vous effectuez une logique métier avec elles. Cependant, les valeurs ThreadLocal sont stockées dans l'objet thread lui-même et dès que le thread est récupéré, ces valeurs disparaissent également.

Ce lien explique très bien l'utilisation de ThreadLocal.


2
Dans cet exemple est un problème majeur: qui est responsable de la fermeture de la connexion? Au lieu de créer cette connexion en tant que valeur initiale, laissez le consommateur de la connexion créer la connexion de manière explicite et la lier au thread via ThreadLocal. Le créateur de la connexion est alors également responsable de la fermeture. Ce n'est pas clair dans cet exemple. La création et la liaison de la connexion peuvent également être masquées dans un cadre simple par quelque chose comme Transaction.begin () et Transaction.end ().
Rick-Rainer Ludwig

2

La classe ThreadLocal en Java vous permet de créer des variables qui ne peuvent être lues et écrites que par le même thread. Ainsi, même si deux threads exécutent le même code et que le code a une référence à une variable ThreadLocal, les deux threads ne peuvent pas voir les variables ThreadLocal de l'autre.

Lire la suite


2

Il existe 3 scénarios d'utilisation d'un assistant de classe comme SimpleDateFormat dans du code multithread, le meilleur étant d'utiliser ThreadLocal

Scénarios

1- Utilisation d'un objet de partage similaire à l'aide d' un mécanisme de verrouillage ou de synchronisation qui rend l'application lente

2- Utilisation comme objet local dans une méthode

Dans ce scénario, si nous avons 4 threads, chacun appelle une méthode 1000 fois, alors nous avons
4000 objets SimpleDateFormat créés et attendant que GC les efface

3- Utiliser ThreadLocal

si nous avons 4 threads et que nous avons donné à chaque thread une instance SimpleDateFormat
, nous avons donc 4 threads , 4 objets de SimpleDateFormat.

Il n'y a pas besoin de mécanisme de verrouillage ni de création et de destruction d'objets. (Bonne complexité temporelle et complexité spatiale)


2

[Pour référence] ThreadLocal ne peut pas résoudre les problèmes de mise à jour de l'objet partagé. Il est recommandé d'utiliser un objet staticThreadLocal qui est partagé par toutes les opérations dans le même thread. [Obligatoire] La méthode remove () doit être implémentée par les variables ThreadLocal, en particulier lors de l'utilisation de pools de threads dans lesquels les threads sont souvent réutilisés. Sinon, cela peut affecter la logique métier suivante et provoquer des problèmes inattendus tels qu'une fuite de mémoire.


1

La mise en cache, vous devez parfois calculer la même valeur beaucoup de temps, donc en stockant le dernier ensemble d'entrées dans une méthode et le résultat, vous pouvez accélérer le code. En utilisant Thread Local Storage, vous évitez d'avoir à penser au verrouillage.


1

ThreadLocal est une fonctionnalité spécialement mise à disposition par JVM pour fournir un espace de stockage isolé pour les threads uniquement. comme la valeur de la variable de portée d'instance sont liés à une instance donnée d'une classe uniquement. chaque objet a ses seules valeurs et ils ne peuvent pas se voir mutuellement. tout comme le concept de variables ThreadLocal, elles sont locales au thread dans le sens des instances d'objet autre thread que celui qui l'a créé, ne peut pas le voir. Vois ici

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;


public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(1000);

// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = ThreadLocal.withInitial(() -> nextId.getAndIncrement());


// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
    return threadId.get();
}

public static void main(String[] args) {

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

    new Thread(() -> IntStream.range(1, 3).forEach(i -> {
        System.out.println(Thread.currentThread().getName() + " >> " + new ThreadId().get());
    })).start();

}
}

1

Threadlocal fournit un moyen très simple de réutiliser des objets à un coût nul.

J'ai eu une situation où plusieurs threads créaient une image de cache mutable, à chaque notification de mise à jour.

J'ai utilisé un Threadlocal sur chaque thread, puis chaque thread aurait juste besoin de réinitialiser l'ancienne image, puis de la mettre à jour à partir du cache à chaque notification de mise à jour.

Les objets réutilisables habituels des pools d'objets ont un coût de sécurité des threads qui leur est associé, alors que cette approche n'en a aucun.


0

Essayez ce petit exemple, pour avoir une idée de la variable ThreadLocal:

public class Book implements Runnable {
    private static final ThreadLocal<List<String>> WORDS = ThreadLocal.withInitial(ArrayList::new);

    private final String bookName; // It is also the thread's name
    private final List<String> words;


    public Book(String bookName, List<String> words) {
        this.bookName = bookName;
        this.words = Collections.unmodifiableList(words);
    }

    public void run() {
        WORDS.get().addAll(words);
        System.out.printf("Result %s: '%s'.%n", bookName, String.join(", ", WORDS.get()));
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new Book("BookA", Arrays.asList("wordA1", "wordA2", "wordA3")));
        Thread t2 = new Thread(new Book("BookB", Arrays.asList("wordB1", "wordB2")));
        t1.start();
        t2.start();
    }
}


Sortie console, si le thread BookA est fait en premier:
Résultat BookA: 'wordA1, wordA2, wordA3'.
Résultat BookB: 'wordB1, wordB2'.

Sortie console, si le thread BookB est effectué en premier:
Résultat BookB: 'wordB1, wordB2'.
Résultat BookA: 'wordA1, wordA2, wordA3'.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.