Quand et comment utiliser une variable ThreadLocal?
Quand dois-je utiliser une variable ThreadLocal
?
Comment est-il utilisé?
22 réponses
une utilisation possible (et courante) est quand vous avez un objet qui n'est pas thread-safe, mais vous voulez éviter synchroniser 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);
}
}
Puisqu'un ThreadLocal
est une référence à des données dans un Thread
donné , vous pouvez vous retrouver avec des fuites de chargement de classe lors de l'utilisation de ThreadLocal
s dans les serveurs d'application qui utilisent des pools de thread. Vous devez être très prudent sur le nettoyage de tout ThreadLocal
s vous get()
ou set()
en utilisant la méthode ThreadLocal
's remove()
.
si vous ne nettoyez pas lorsque vous avez terminé, toutes les références qu'il tient aux classes chargées dans le cadre d'une webapp déployée sera rester dans le tas permanent et ne sera jamais obtenir des ordures collectées. Redéploiement / undeploying le webapp ne nettoiera pas chaque Thread
de référence à votre classe webapp (es) puisque 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 ramassée.
vous finirez avec des exceptions de mémoire en raison de java.lang.OutOfMemoryError: PermGen space
et après quelques googling sera probablement juste augmenter -XX:MaxPermSize
au lieu de corriger le bug.
si vous éprouvez ces problèmes, vous pouvez déterminer quel fil et quelle classe retiennent ces références en utilisant L'Analyseur de mémoire D'Eclipse et/ou en suivant le guide de Frank Kieviet et suivi .
mise à jour: redécouvert Alex Vasseur's blog entry cela m'a aidé à trouver quelques problèmes ThreadLocal
que j'avais.
de nombreux frameworks utilisent des ThreadLocals pour maintenir un contexte lié au thread courant. Par exemple, lorsque la transaction courante est stockée dans un ThreadLocal, vous n'avez pas besoin de la passer en paramètre à travers 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 requête actuelle et la session dans un ThreadLocal, de sorte que l'application a un accès facile à eux. Avec Guice, vous pouvez utiliser ThreadLocals lors de la mise en œuvre "151910920 personnalisé" étendues pour l'injection des objets (Guice par défaut servlet étendues plus probablement les utiliser en tant que bien).
ThreadLocals sont une sorte de variables globales (bien que légèrement moins mauvais parce qu'ils sont limités à un fil), donc vous devriez faire attention en les utilisant 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 le sont pas. plus nécessaire et que l'utilisation incorrecte de L'API ne sera pas possible (par exemple comme ceci ). Les ThreadLocals peuvent être utilisés 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 cas de ce genre; ils sont documentés ici sous "champs statiques et Variables globales").
en Java, si vous avez une donnée qui peut varier par thread, vos choix sont de passer cette donnée autour de chaque méthode qui a besoin (ou peut avoir besoin) elle, ou d'associer la donnée avec le thread. Passer le zéro partout peut être réalisable si toutes vos méthodes ont déjà besoin de passer autour d'une variable de "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 non-filetée monde, vous pourrait résoudre le problème avec Java équivalent d'une variable globale. Dans un mot fileté, l'équivalent d'une variable globale est une variable thread-local.
essentiellement, quand vous avez besoin d'une valeur de la variable dépend du fil courant et il n'est pas pratique pour vous d'attacher la valeur au fil d'une autre manière (par exemple, sous-classement fil).
un cas typique est où un autre framework a créé le thread que votre code est en cours d'exécution, par exemple un conteneur servlet, ou où il est juste plus logique d'utiliser ThreadLocal parce que votre variable est alors "à sa place logique" (plutôt qu'une variable suspendue à une sous-classe de Thread ou dans une autre carte de hachage).
sur mon site web, j'ai d'autres discussion et des exemples de quand utiliser ThreadLocal qui peuvent également être d'intérêt.
certaines personnes préconisent L'utilisation de ThreadLocal comme une façon 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 de tels cas, vérifiez que vous recevez vraiment une prestation!
il y a un très bon exemple dans le livre la concurrence Java dans la pratique . Où l'auteur ( Joshua Bloch ) explique comment le confinement du fil est l'un des moyens les plus simples pour obtenir 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 mentionné livre mais le code 3.10 est manquant car il n'est pas très important de comprendre où ThreadLocal devrait être utilisé.
Thread-les variables locales sont souvent utilisés afin d'empêcher le partage de conceptions basées sur mutable Singletons ou des variables globales. Par exemple, une application mono-threadée pourrait maintenir une connexion de base de données globale qui est initialisée au démarrage pour éviter d'avoir à passer une connexion à chaque méthode. Comme les connexions JDBC peuvent ne pas être thread-safe, l'application multithread qui utilise une connexion globale sans coordination supplémentaire n'est pas non plus sécurisée. 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 cadres d'application. Par exemple, les conteneurs J2EE associent un contexte de transaction avec un thread d'exécution pour la durée d'un appel EJB. Ceci est facilement implémenté en utilisant un Thread statique-Local contenant le contexte de la transaction: lorsque le code framework doit déterminer quelle transaction est en cours, il récupère le contexte de la transaction à partir de ce ThreadLocal. Ceci est pratique en ce qu'il réduit le besoin de passer des informations de contexte d'exécution dans chaque méthode, mais couple n'importe quel code qui utilise ce mécanisme au framework.
il est facile d'abuser ThreadLocal en traitant sa propriété de confinement de fil comme licence pour utiliser des variables globales ou comme moyen de créer des arguments de méthode "cachés". Comme les variables globales, les variables thread-local peuvent nuire à la réutilisabilité et introduire des couplages cachés entre les classes, et doivent donc être utilisées avec soin.
la documentation le dit très bien:" chaque thread qui accède [à une variable thread-local] (via sa méthode get ou set) a sa propre copie initialisée et indépendante de la variable".
vous utilisez un quand chaque fil doit avoir sa propre copie de quelque chose. Par défaut, les données sont partagées entre les threads.
peut conserver un pool de threads, et un ThreadLocal
var doit être supprimé avant la réponse au client, donc le thread courant peut être réutilisé par la requête suivante.
-
ThreadLocal en Java a été introduite sur la JDK 1.2, mais a été plus tard generified dans le JDK 1.5 à introduire le type de sécurité sur ThreadLocal variable.
-
ThreadLocal peut être associé à Thread scope, tout le code qui est exécuté par Thread a accès à ThreadLocal variables mais deux thread ne peut pas voir les autres ThreadLocal variable.
-
chaque fil détient une exclusivité copie de la variable ThreadLocal qui devient éligible à la collecte des ordures après la fin du fil ou la mort, Normalement ou en raison de toute Exception, étant donné que cette variable ThreadLocal n'a pas d'autres références en direct.
-
les variables ThreadLocal en Java sont généralement des champs statiques privés dans les Classes et maintiennent leur état dans le Thread.
lire la suite: http://javarevisited.blogspot.com/2012/05/how-to-use-threadlocal-in-java-benefits.html#ixzz2XltgbHTK
deux cas d'utilisation où la variable threadlocal peut être utilisée -
1-Lorsque nous avons l'obligation d'associer l'État à un thread (par exemple, un identifiant utilisateur ou un identifiant de Transaction). Cela se produit généralement avec une application web que chaque requête allant à un servlet a un transactionID unique associé avec elle.
// 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 que ici la méthode withInitial est implémentée en utilisant l'expression lambda.
2 - un Autre exemple d'utilisation nous voulons avoir une instance thread safe et 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é. Depuis SimpleDateFormat n'est pas thread sûr, nous devons donc fournir un mécanisme pour le rendre thread sûr.
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()));
}
}
vous devez être très prudent avec le motif ThreadLocal. Il y a des côtés majeurs comme Phil l'a mentionné, mais un qui n'a pas été mentionné est de s'assurer que le code qui configure le contexte ThreadLocal n'est pas "re-entrant"."
de mauvaises choses peuvent se produire lorsque le code qui définit l'information est lancé une deuxième ou une troisième fois parce que l'information sur votre thread peut commencer à muter quand vous ne vous y attendiez pas. Alors prenez soin de vous assurer que le ThreadLocal l'information n'a pas été mis en avant à nouveau.
rien de vraiment nouveau ici, mais j'ai découvert aujourd'hui que ThreadLocal
est très utile lors de L'utilisation de la Validation Bean dans une application web. Les messages de Validation sont localisés, mais par défaut utilisez Locale.getDefault()
. Vous pouvez configurer le Validator
avec un MessageInterpolator
différent , mais il n'y a aucun moyen de spécifier le Locale
quand vous appelez validate
. Vous pouvez donc créer un statique ThreadLocal<Locale>
(ou, mieux encore, un conteneur avec d'autres choses que vous pourriez avoir besoin d'être ThreadLocal
et alors, faites en sorte que votre MessageInterpolator
personnalisé choisisse le Locale
. L'étape suivante consiste à écrire un ServletFilter
qui utilise une valeur de session ou request.getLocale()
pour choisir la locale et la stocker dans votre référence ThreadLocal
.
comme a été mentionné par @unknown (google), son usage est de définir une variable globale dans laquelle la valeur référencée peut être unique dans chaque thread. Ses usages impliquent généralement le stockage d'une sorte d'information contextuelle liée au fil courant de l'exécution.
nous l'utilisons dans un environnement Java EE pour passer l'identité de l'utilisateur à des classes qui ne sont pas Java EE aware (ne pas avoir accès à HttpSession, ou le EJB SessionContext). De cette façon le code, qui fait usage de l'identité pour les opérations basées sur la sécurité, peut accéder à l'identité de n'importe où, sans avoir à le passer explicitement dans chaque appel de méthode.
le cycle de requête/réponse des opérations dans la plupart des appels Java EE rend ce type d'utilisation facile car il donne des points d'entrée et de sortie bien définis pour régler et désactiver le ThreadLocal.
ThreadLocal assurera l'accès à l'objet mutable par le multiple fils dans la méthode non synchronisée est synchronisé, signifie faire la mutable objet immuable dans la méthode.
Cette est obtenu en donnant une nouvelle instance d'objet mutable pour chaque thread essayez d'y accéder. Il s'agit donc d'une copie locale de chaque fil. C'est certains hack on making instance variable dans une méthode à accéder comme un variable locale. Comme vous le savez méthode variable locale est seulement disponible pour le thread, une différence est; les variables locales de la méthode ne seront pas disponible pour le thread une fois que l'exécution de la méthode est terminée où comme mutable objet partagé avec threadlocal sera disponible dans plusieurs des méthodes jusqu'à ce qu'on nettoie.
Par Définition:
la classe ThreadLocal en Java vous permet de créer des variables qui peuvent ne soit lu et écrit que par le même fil. Ainsi, même si deux fils d'exécuter le même code, et le code a une référence à une ThreadLocal variable, puis les deux threads ne peuvent pas voir l'autre ThreadLocal variables.
chaque Thread
en java contient ThreadLocalMap
en elle.
Où
Key = One ThreadLocal object shared across threads.
value = Mutable object which has to be used synchronously, this will be instantiated for each thread.
réaliser le ThreadLocal:
crée 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 vont travailler sur threadlocal instance au lieu de mutable object.
si getter () de threadlocal n'a pas trouvé de valeur dans threadlocalmap du Thread
; alors il va invoquer la valeur initiale () pour obtenir sa valeur privée exemplaire à l'égard de la 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 qui vérifiera le currentThread.threadLocalMap
contient cette (threadlocal) instance.
Si oui, retourner la valeur (SimpleDateFormat) pour l'instance threadlocale correspondante
sinon, ajoutez la map avec cette instance threadlocal, initialValue ().
ci-Joint la sécurité du thread obtenu sur cette classe de mutable; par chaque thread fonctionne 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 différent SimpleDateFormat instance comme valeur.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
quand?
quand un objet n'est pas thread-safe, au lieu de la synchronisation qui entrave l'évolutivité, donnez un objet à chaque thread et gardez-le Portée thread, qui est ThreadLocal. Un des objets les plus souvent utilisés, mais pas sûr, est la connexion à la base de données et JMSConnection.
comment ?
un exemple est Spring framework utilise ThreadLocal fortement pour gérer les transactions en coulisse en gardant ces objets de connexion dans les variables ThreadLocal. À un niveau élevé, lorsqu'une transaction est lancée, elle obtient la connexion ( et désactive l'auto commit ) et la conserve dans ThreadLocal. sur d'autres appels db il utilise la même connexion pour communiquer avec db. À la fin, il prend la connexion à partir de ThreadLocal et engage ( OU Renvoie ) la transaction et libère la connexion.
je pense que log4j utilise aussi ThreadLocal pour maintenir MDC.
ThreadLocal
est utile, quand vous voulez avoir un État qui ne devrait pas être partagé entre les différents threads, mais il devrait être accessible à partir de chaque thread pendant toute sa durée de vie.
par exemple, imaginez une application web, où chaque requête est servie par un thread différent. Imaginez que, pour chaque demande, vous avez besoin d'un morceau de données plusieurs fois, ce qui est assez onéreux. Toutefois, ces données peuvent avoir changé pour chaque demande reçue, ce qui ça veut dire que tu ne peux pas utiliser une cache. Une solution simple et rapide à ce problème serait d'avoir une variable ThreadLocal
permettant l'accès à ces données, de sorte que vous n'ayez à les calculer qu'une seule fois pour chaque demande. Bien sûr, ce problème peut également être résolu sans l'utilisation de ThreadLocal
, mais je l'a inventé à des fins d'illustration.
cela dit, avoir à l'esprit que ThreadLocal
s sont essentiellement une forme d'état global. En conséquence, il a de nombreuses autres implications et doit être utilisé uniquement après avoir examiné toutes les autres solutions possibles.
Thread-les variables locales sont souvent utilisés afin d'empêcher le partage de conceptions basées sur des Singletons mutables ou des variables globales.
il peut être utilisé dans des scénarios comme faire une connexion JDBC séparée pour chaque thread lorsque vous n'utilisez pas un Pool de connexion.
private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
quand vous appelez getConnection, il renverra une connexion associée à ce thread.La même chose peut être faite avec d'autres propriétés comme dateformat, le contexte de transaction que vous Je ne veux pas partager entre les fils.
vous auriez pu également utiliser des variables locales pour le même, mais ces ressources prennent généralement du temps dans la création,de sorte que vous ne voulez pas les créer encore et encore chaque fois que vous effectuez une certaine logique commerciale avec eux. Cependant, les valeurs ThreadLocal sont stockées dans l'objet thread lui-même et dès que le thread est collecté, ces valeurs sont perdues aussi.
Ce lien explique l'utilisation de ThreadLocal très bien.
depuis la version Java 8, Il existe une façon plus déclarative d'initialiser ThreadLocal
:
ThreadLocal<Cipher> local = ThreadLocal.withInitial(() -> "init value");
Jusqu'à la sortie de Java 8, vous deviez faire ce qui suit:
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 qui est utilisée pour ThreadLocal
ne prend aucun paramètre, vous pouvez simplement utiliser des références de méthode (introduites en Java 8):
class NotThreadSafe {
// no parameters
public NotThreadSafe(){}
}
ThreadLocal<NotThreadSafe> container = ThreadLocal.withInitial(NotThreadSafe::new);
Note:
L'évaluation est paresseuse puisque vous passez java.util.function.Supplier
lambda qui est évalué seulement quand ThreadLocal#get
est appelé mais la valeur n'a pas été évaluée auparavant.
la mise en cache, parfois vous devez calculer la même valeur beaucoup de temps donc en stockant le dernier ensemble d'entrées à une méthode et le résultat vous pouvez accélérer le code vers le haut. En utilisant le stockage local de Thread vous évitez d'avoir à penser à la fermeture.
ThreadLocal est une fonctionnalité spécialement fournie par JVM pour fournir un espace de stockage isolé pour les threads seulement. comme la valeur de l'instance d'étendue variable sont liés à une instance d'une classe seulement. chaque objet a ses seules valeurs et ils ne peuvent pas voir les autres valeurs. ainsi est le concept de variables ThreadLocal, elles sont locales au thread dans le sens d'instances d'objet autre thread sauf pour 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 facile d'atteindre la réutilisabilité des objets avec un coût zéro.
j'ai eu une situation où plusieurs threads créaient une image de cache mutable, sur chaque notification de mise à jour.
j'ai utilisé un Threadlocal sur chaque thread, et ensuite chaque thread aurait juste besoin de réinitialiser l'Ancienne image et de la mettre à jour à nouveau à partir du cache sur chaque notification de mise à jour.
Habituel des objets réutilisables de l'objet piscines ont les coûts de sécurité des fils associés à eux, alors que cette approche n'a aucun.
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, alors les deux threads ne peuvent pas voir les variables ThreadLocal de l'autre.