Question

Quand devrais-je utiliser un ThreadLocal variable?

Comment est-il utilisé?

Était-ce utile?

La solution

Une utilisation possible (et courante) est lorsque vous avez un objet qui n'est pas thread-safe, mais que vous voulez éviter synchronisation de 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 .

Autres conseils

Etant donné qu'un ThreadLocal est une référence à des données appartenant à un Thread donné, vous pouvez vous retrouver avec des fuites lors du chargement de classes lorsque vous utilisez get() dans des serveurs d'applications utilisant des pools de threads. Vous devez faire très attention de ne pas nettoyer les set() s ou remove() en utilisant la méthode java.lang.OutOfMemoryError: PermGen space du -XX:MaxPermSize.

Si vous ne nettoyez pas lorsque vous avez terminé, toutes les références qu'elle contient aux classes chargées dans le cadre d'une application Web déployée resteront dans tas permanent et ne le sera jamais faire ramasser les ordures. Le redéploiement / annulation du déploiement de l’application Web ne supprime pas chaque référence <=> aux classes de votre application Web, car le <=> n’appartient pas à votre application Web. Chaque déploiement successif créera une nouvelle instance de la classe qui ne sera jamais nettoyée.

Vous vous retrouverez avec des exceptions d'insuffisance de mémoire en raison de <=> et après quelques recherches sur Google augmenterez probablement <=> au lieu de corriger le bogue.

Si vous rencontrez ces problèmes, vous pouvez déterminer le thread et la classe conservant ces références en utilisant Eclipse Memory Analyzer et / ou en suivant Guide de Frank Kieviet et suivi .

Mise à jour: l'article de blog d'Alex Vasseur problèmes que je rencontrais.

De nombreux frameworks utilisent ThreadLocals pour maintenir un 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 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 y ait facilement accès. Avec Guice, vous pouvez utiliser ThreadLocals lors de la mise en oeuvre des étendues personnalisées pour les objets injectés (la valeur par défaut de Guice < a href = "https://github.com/google/guice/wiki/Scopes" rel = "nofollow noreferrer"> Les étendues de servlet les utilisent probablement aussi).

Les ThreadLocals sont une sorte de variables globales (bien qu’un peu moins pervers parce qu’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 manière à ce 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 ici sous &"; Champs statiques et variables globales & ";).

En Java, si vous avez une donnée qui peut varier d’un thread à l’autre, vous avez le choix de transmettre cette donnée à chaque méthode qui en a besoin (ou peut en avoir besoin), ou d’associer le datum au thread. Passer la donnée partout peut être faisable si toutes vos méthodes doivent déjà passer un même & Quot; contexte & Quot; variable.

Si ce n'est pas le cas, vous ne voudrez peut-être pas encombrer les signatures de votre 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 thread-locale.

Il existe un très bon exemple dans le livre Java Concurrency in Practice . Où l'auteur ( Joshua Bloch ) explique pourquoi le confinement de threads est l'un des moyens les plus simples de sécuriser les threads et ThreadLocal est un moyen plus formel de maintenir le confinement des threads. En fin de compte, il explique également comment les gens peuvent en abuser en l’utilisant comme variable globale.

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 locales aux threads 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 à un seul thread peut conserver une connexion à une base de données globale qui est initialisée au démarrage pour éviter de devoir passer une connexion à chaque méthode. Etant 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 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 d'infrastructure a besoin de déterminer quelle transaction est en cours d'exécution, il extrait le contexte de la transaction à partir de ce ThreadLocal. Ceci est pratique en ce sens qu’il réduit le besoin de transmettre des informations de contexte d’exécution à chaque méthode, mais associe tout code qui utilise ce mécanisme au framework.

     

Il est facile d’abuser de ThreadLocal en considérant sa propriété de confinement de threads comme une licence permettant d’utiliser des variables globales ou comme un moyen de créer & # 8220; hidden & # 8221; arguments de méthode. Comme les variables globales, les variables locales aux unités d'exécution peuvent empêcher la possibilité de réutilisation et introduire des couplages cachés entre les classes. Elles doivent donc être utilisées avec précaution.

Essentiellement, lorsque vous avez besoin qu'une valeur de variable dépende du thread actuel et que vous ne puissiez pas attacher la valeur au thread d'une autre manière (par exemple, fil de sous-classe).

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

Sur mon site Web, il existe d'autres discussions et exemples d'utilisation de ThreadLocal cela peut aussi être intéressant.

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

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

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

Le serveur Webapp peut conserver un pool de threads. Une ThreadLocal var doit être supprimée avant toute réponse au client. Ainsi, le thread actuel peut être réutilisé lors de la prochaine requête.

  1. ThreadLocal en Java a été introduit sur JDK 1.2 mais a ensuite été 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 de Thread, tout le code exécuté par Thread a accès aux variables ThreadLocal mais deux threads ne peuvent pas se voir. Variable ThreadLocal.

  3. Chaque thread contient une copie exclusive de la variable ThreadLocal qui devient éligible à la récupération de place après la fin ou la fin du thread, normalement ou suite à une exception, étant donné que la variable ThreadLocal ne possède aucune autre référence active.

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

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

Deux cas d'utilisation de la variable threadlocal -
1- Lorsque nous devons associer un état à un fil (par exemple, un ID utilisateur ou un ID de transaction). Cela se produit généralement avec une application Web et chaque requête adressé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 à l'aide de 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 associé à la synchronisation est plus élevé. Un tel cas est lorsque SimpleDateFormat est utilisé. Étant donné que SimpleDateFormat n'est pas thread-safe, nous devons fournir un mécanisme pour le rendre thread-safe.

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()));
    } 

}

Depuis la version Java 8, il n'y a plus de manière déclarative d'initialiser ThreadLocal:

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

Jusqu'à la version 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 java.util.function.Supplier ne prend aucun paramètre, vous pouvez simplement utiliser les 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 puisque vous passez ThreadLocal#get lambda qui est évalué uniquement lorsque <=> est appelé mais que la valeur n'a pas été évaluée auparavant.

Vous devez faire très attention au motif ThreadLocal. Phil a mentionné certains inconvénients majeurs, mais l'un d'entre eux n'a pas été mentionné: il s'agit de s'assurer que le code qui configure le contexte ThreadLocal n'est pas & "Réentrant. &";

Des problèmes peuvent survenir lorsque le code qui définit les informations est exécuté une deuxième ou une troisième fois car les informations de votre thread peuvent commencer à muter lorsque vous ne vous y attendiez pas. Veillez donc à ce que les informations ThreadLocal n’aient pas été définies avant de les définir à nouveau.

quand?

Lorsqu'un objet n'est pas thread-safe, au lieu d'une synchronisation qui entrave l'évolutivité, attribuez un objet à chaque thread et conservez-y la portée du thread, à savoir ThreadLocal. L'un des objets les plus souvent utilisés, mais qui ne sont pas thread-safe, sont Database Connection et JMSConnection.

Comment?

Par exemple, Spring utilise fréquemment ThreadLocal pour gérer les transactions en arrière-plan en conservant ces objets de connexion dans des variables ThreadLocal. À un niveau élevé, lorsqu'une transaction est démarrée, elle obtient la connexion (et désactive la validation automatique) et la conserve dans ThreadLocal. sur d'autres appels de base de données, 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.

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

Par exemple, imaginez une application Web où chaque demande est traitée 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. Toutefois, 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 consisterait à avoir une <=> variable détenant un accès à ces données, de sorte que vous ne deviez la calculer qu'une seule fois pour chaque demande. Bien sûr, ce problème peut également être résolu sans l'utilisation de <=>, mais je l'ai conçu à des fins d'illustration.

Cela dit, gardez à l’esprit que <=> sont essentiellement une forme d’état global. En conséquence, cela a de nombreuses autres implications et ne devrait être utilisé qu'après avoir examiné toutes les autres solutions possibles.

Rien de bien nouveau ici, mais j’ai découvert aujourd’hui que ThreadLocal est très utile lors de l’utilisation de la validation de beans dans une application Web. Les messages de validation sont localisés, mais utilisez par défaut Locale.getDefault(). Vous pouvez configurer le Validator avec un autre MessageInterpolator, mais vous ne pouvez pas spécifier le Locale lorsque vous appelez validate. Vous pouvez donc créer un statique ThreadLocal<Locale> (ou, mieux encore, un conteneur général contenant d’autres éléments dont vous pourriez avoir besoin ServletFilter, puis demandez à votre coutume request.getLocale() de choisir le <=>. La prochaine étape consiste à écrire un <=> qui utilise une valeur de session ou <=> pour choisir les paramètres régionaux et les stocker dans votre <=> référence.

Comme 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 en cours.

Nous l'utilisons dans un environnement Java EE pour transmettre l'identité de l'utilisateur à des classes non conscientes de Java EE (nous n'avons pas accès à HttpSession ni à l'EJB SessionContext). De cette manière, le code, qui utilise l’identité pour des opérations basées sur la sécurité, peut accéder à l’identité depuis n’importe où, sans avoir à la transmettre explicitement à chaque appel de méthode.

Le cycle d'opérations requête / 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 annuler la définition du ThreadLocal.

  

ThreadLocal assurera l’accès à l’objet mutable par le multiple   les threads dans la méthode non synchronisée sont synchronisés, ce qui signifie   l'objet mutable doit être immuable dans la méthode.

Cette   est obtenu en donnant une nouvelle instance d'objet mutable pour chaque thread   essayez d'y accéder. Donc, il est une copie locale à chaque thread. Ça me ressemble tellement   bidouillez pour créer une variable d'instance dans une méthode accessible comme un   variable locale. Comme vous le savez, la variable locale de la méthode est uniquement disponible   pour le fil, une différence est; méthode variables locales ne seront pas   disponible pour le thread une fois que l'exécution de la méthode est terminée là où elle est mutable   objet partagé avec threadlocal sera disponible sur plusieurs   méthodes jusqu'à ce que nous le nettoyons.

Par définition:

  

La classe ThreadLocal en Java vous permet de créer des variables pouvant   seulement être lu et écrit par le même fil. Ainsi, même si deux threads   exécutent le même code, et le code fait référence à un   Variable ThreadLocal, les deux threads ne peuvent pas se voir   Variables ThreadLocal.

Chaque Thread en java contient ThreadLocalMap en elle.

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

Réalisation du ThreadLocal:

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

Si getter () de threadlocal n’a trouvé aucune valeur dans le threadlocalmap du wrapper.getDateFormatter(); il invoquera alors 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 threadlocal.get() appellera currentThread.threadLocalMap et vérifiera si <=> contient cette instance (threadlocal).
Si oui, renvoyer la valeur (SimpleDateFormat) de l'instance threadlocal correspondante
sinon, ajoutez la carte avec cette instance locale, initialValue ().

La sécurité des threads est maintenant atteinte sur cette classe mutable; par chaque thread travaille avec sa propre instance mutable mais avec la même instance ThreadLocal. Tous les threads partageront la même instance ThreadLocal en tant que clé, mais une instance SimpleDateFormat différente en tant que valeur.

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

Les variables locales aux threads sont souvent utilisées pour empêcher le partage dans les conceptions basées sur Singletons ou variables globales mutables.

Il peut être utilisé dans des scénarios tels que l'établissement d'une connexion JDBC séparée 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 renverra une connexion associée à ce fil. La même chose peut être faite avec d'autres propriétés telles que dateformat, contexte de transaction que vous ne voulez pas partager entre les threads.

Vous auriez peut-être également utilisé des variables locales pour la même chose, mais ces ressources prennent généralement du temps lors de la création. Vous ne souhaitez donc pas les créer encore et encore à chaque fois que vous exécutez 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 collecté, ces valeurs ont également disparu.

Cette link explique très bien l'utilisation de ThreadLocal.

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 celui-ci fait référence à une variable ThreadLocal, ils ne peuvent pas voir les variables ThreadLocal l'un de l'autre.

En savoir plus

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

Mise en cache, vous devez parfois calculer la même valeur plusieurs fois. Ainsi, en stockant le dernier jeu 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 de penser au verrouillage.

ThreadLocal est une fonctionnalité spécialement conçue par JVM pour fournir un espace de stockage isolé aux threads uniquement. comme la valeur d'instance, les variables couvertes ne sont liées qu'à une instance donnée d'une classe. chaque objet a ses seules valeurs et ils ne peuvent pas se voir. il en va de même pour le concept de variables ThreadLocal, elles sont locales pour le thread au sens d'instances d'objet autre que thread, à l'exception de celui qui l'a créé, ne peut pas le voir. Voir 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();

}
}

Threadlocal fournit un moyen très simple d’atteindre la réutilisation des objets avec un coût nul.

J'étais dans 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 simplement besoin de réinitialiser l'ancienne image, puis de la remettre à jour à partir du cache à chaque notification de mise à jour.

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

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

Scénarios

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

2 - utiliser un objet local dans une méthode

Dans ce scénario, si nous avons 4 threads , chacun appelle une méthode 1000 fois, nous aurons alors

4000 SimpleDateFormat objet créé et dans l'attente de son effacement par GC

3 - Utilisation de ThreadLocal

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

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top