Introduction
ThreadLocal fournit un moyen selon lequel, dans un environnement multithread, chaque thread peut avoir ses propres données uniques et peut être transmis de haut en bas pendant tout le processus d'exécution du thread.
1. Démonstration d'utilisation
De nombreux étudiants n'ont peut-être jamais utilisé ThreadLocal auparavant. Démontrons d'abord l'utilisation de ThreadLocal :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
static final ThreadLocal<Map<String, String>> context = new ThreadLocal<>();
@Test
public void testThread() {
Map<String, String> contextMap = context.get();
if (CollectionUtils.isEmpty(contextMap)) {
contextMap = Maps.newHashMap();
}
contextMap.put( "key1" , "value1" );
context.set(contextMap);
log.info( "key1,value1被放到上下文中" );
getFromComtext();
}
private String getFromComtext() {
String value1 = context.get().get( "key1" );
log.info( "从 ThreadLocal 中取出上下文,key1 对应的值为:{}" , value1);
return value1;
}
demo.ninth.ThreadLocalDemo - key1,value1被放到上下文中
demo.ninth.ThreadLocalDemo - 从 ThreadLocal 中取出上下文,key1 对应的值为:value1
|
Copier après la connexion
Comme vous pouvez le voir sur les résultats en cours, la valeur correspondant à key1 a été. obtenu à partir du contexte.
La méthode getFromComtext n'accepte aucun paramètre d'entrée. Grâce à cette ligne de code, context.get().get("key1"), la valeur de key1 est obtenue à partir du contexte. ThreadLocal sous-jacent est implémenté.
2. Structure de classe
2.1. Génériques de classe
ThreadLocal définit la classe avec des génériques, indiquant que ThreadLocal peut stocker des données dans n'importe quel format :
1 | public class ThreadLocal<T> {}
|
Copier après la connexion
2.2.2.2. Attributs de clés, examinons-les un par un :
1 2 3 4 5 6 7 8 9 | private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static AtomicInteger nextHashCode = new AtomicInteger();
|
Copier après la connexion
Copier après la connexion
Il existe un autre attribut important : ThreadLocalMap Lorsqu'un thread a plusieurs ThreadLocals, un conteneur est nécessaire pour gérer plusieurs ThreadLocals. Le rôle de ThreadLocalMap est de gérer plusieurs ThreadLocals dans. le fil.
2.2.1, ThreadLocalMap
ThreadLocalMap lui-même est une structure Map simple, la clé est ThreadLocal, la valeur est la valeur enregistrée par ThreadLocal, la couche inférieure est la structure de données du tableau, le code source est le suivant :
1 2 3 4 5 6 7 8 9 | private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static AtomicInteger nextHashCode = new AtomicInteger();
|
Copier après la connexion
Copier après la connexion
Voir à partir du code source que ThreadLocalMap est en fait une structure Map simple, la couche inférieure est un tableau, avec une taille d'initialisation et une taille de seuil d'expansion. Les éléments du tableau sont Entry, la clé d'Entrée est la référence de ThreadLocal. , et la valeur est la valeur de ThreadLocal.
3. Comment ThreadLocal réalise-t-il l'isolation des données entre les threads ?
ThreadLocal est thread-safe et nous pouvons l'utiliser en toute confiance, principalement parce que ThreadLocalMap est une propriété des threads, comme. suit :
De l'image ci-dessus, nous pouvons voir que ThreadLocals.ThreadLocalMap et InheritableThreadLocals.ThreadLocalMap sont des propriétés de threads, donc les ThreadLocals de chaque thread sont isolés et exclusifs.
Lorsque le thread parent crée un thread enfant, il copiera la valeur de HeitableThreadLocals, mais ne copiera pas la valeur de threadLocals. Le code source est le suivant :
D'après la figure ci-dessus, nous pouvons voir que lorsque le thread est créé, il sera copié. La valeur de l'attribut HeitableThreadLocals du thread parent est copiée.
4. Méthode Set
La fonction principale de la méthode set est de définir la valeur dans le ThreadLocal actuel. Si le type générique du ThreadLocal actuel est Map, alors il s'agit de définir la carte dans le ThreadLocal actuel. Le code est le suivant :
1 2 3 4 5 6 7 8 9 10 11 | public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
|
Copier après la connexion
La logique du code est relativement claire. Jetons un coup d'œil au code source de ThreadLocalMap.set, comme suit :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return ;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return ;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
|
Copier après la connexion
Nous prêtons attention à quelques points dans le code source ci-dessus :
utilise un AtomicInteger incrémentiel comme hashCode de ThreadLocal ; La formule pour calculer la position de l'index du tableau est : hashCode modulo la taille du tableau. Puisque hashCode continue d'augmenter, différents hashCode calculeront très probablement la position d'index du tableau. même tableau (mais ne vous inquiétez pas pour cela. Dans les projets réels, ThreadLocal est très rare et fondamentalement pas de conflit) S'il y a déjà une valeur à la position d'index i calculée par hashCode, elle commencera à partir de i et continuez à chercher en arrière jusqu'à +1 jusqu'à ce qu'il trouve une position d'index vide, et placez le ThreadLocal actuel comme clé Go in. Heureusement, lorsque nous utilisons ThreadLocals dans le travail quotidien, nous n'utilisons souvent que 1 à 2 ThreadLocals, et la probabilité de tableaux en double calculés par hachage n'est pas très élevée.
La stratégie de résolution des conflits de position des éléments du tableau pendant l'ensemble affecte également la méthode get. Examinons ensemble la méthode get.
5. Méthode get
La méthode get obtient principalement la valeur stockée dans le ThreadLocal actuel à partir de ThreadLocalMap :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings( "unchecked" )
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
|
Copier après la connexion
Jetons ensuite un coup d'œil à la méthode getEntry de ThreadLocalMap. suit :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
|
Copier après la connexion
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
|
Copier après la connexion
Commentaires dans le code source get logic Il a été écrit très clairement, nous ne le répéterons donc pas.
6. Expansion
Lorsque le nombre de ThreadLocals dans ThreadLocalMap dépasse le seuil, ThreadLocalMap commencera à se développer :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null;
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count ++;
}
}
}
setThreshold(newLen);
size = count ;
table = newTab;
}
|
Copier après la connexion
Les annotations du code source sont également relativement claires. deux points :
Après l'expansion, la taille du tableau est le double de celle du tableau d'origine ; Il n'y a absolument aucun problème de sécurité des threads lors de l'expansion, car ThreadLocalMap est un attribut du thread, et un thread peut ne fonctionne que sur ThreadLocalMap en même temps, car le même thread exécutant la logique métier doit être en série, donc l'exploitation de ThreadLocalMap doit également être en série.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!