Wie wird ThreadLocal implementiert? Ist es in Java implementiert (unter Verwendung einer gleichzeitigen Zuordnung von ThreadID zu Objekt) oder verwendet es einen JVM-Hook, um es effizienter zu machen?
Wie wird Javas ThreadLocal unter der Haube implementiert?
Reißer234
dimo414
Alle Antworten hier sind richtig, aber ein wenig enttäuschend, da sie etwas darüber hinwegtäuschen, wie klug sie sind ThreadLocal
Die Implementierung von ist. Ich habe mir gerade die angeschaut Quellcode für ThreadLocal
und war angenehm beeindruckt von der Umsetzung.
Die naive Implementierung
Wenn ich Sie bitten würde, a ThreadLocal<T>
Klasse angesichts der im Javadoc beschriebenen API, was würden Sie tun? Eine erste Implementierung wäre wahrscheinlich a ConcurrentHashMap<Thread,T>
verwenden Thread.currentThread()
als seinen Schlüssel. Dies würde ziemlich gut funktionieren, hat aber einige Nachteile.
- Thread-Konflikt –
ConcurrentHashMap
ist eine ziemlich schlaue Klasse, aber sie muss letztendlich immer noch verhindern, dass mehrere Threads in irgendeiner Weise damit herumspielen, und wenn verschiedene Threads sie regelmäßig treffen, wird es zu Verlangsamungen kommen. - Behält dauerhaft einen Zeiger sowohl auf den Thread als auch auf das Objekt, selbst nachdem der Thread beendet wurde und GC’ed werden könnte.
Die GC-freundliche Implementierung
Ok, versuchen Sie es noch einmal. Lassen Sie uns das Problem der Garbage Collection mithilfe von lösen schwache Referenzen. Der Umgang mit WeakReferences kann verwirrend sein, aber es sollte ausreichen, eine Karte wie folgt zu verwenden:
Collections.synchronizedMap(new WeakHashMap<Thread, T>())
Oder wenn wir verwenden Guave (und wir sollten es sein!):
new MapMaker().weakKeys().makeMap()
Das bedeutet, sobald niemand mehr an dem Thread festhält (was bedeutet, dass er fertig ist), kann der Schlüssel/Wert der Garbage Collection unterzogen werden, was eine Verbesserung darstellt, aber immer noch nicht das Problem der Thread-Konkurrenz anspricht, was bisher unser bedeutet ThreadLocal
ist nicht so erstaunlich von einer Klasse. Außerdem, wenn sich jemand zum Festhalten entschließt Thread
Objekte, nachdem sie fertig waren, würden sie niemals GC’ed werden, und daher auch unsere Objekte nicht, obwohl sie jetzt technisch nicht erreichbar sind.
Die clevere Umsetzung
Wir haben darüber nachgedacht ThreadLocal
als Zuordnung von Threads zu Werten, aber vielleicht ist das nicht die richtige Art, darüber nachzudenken. Anstatt es als eine Zuordnung von Threads zu Werten in jedem ThreadLocal-Objekt zu betrachten, was wäre, wenn wir es als eine Zuordnung von ThreadLocal-Objekten zu Werten betrachten würden in jedem Thread? Wenn jeder Thread die Zuordnung speichert und ThreadLocal lediglich eine nette Schnittstelle zu dieser Zuordnung bereitstellt, können wir alle Probleme der vorherigen Implementierungen vermeiden.
Eine Implementierung würde in etwa so aussehen:
// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()
Sie müssen sich hier keine Gedanken über Parallelität machen, da immer nur ein Thread auf diese Map zugreift.
Die Java-Entwickler haben hier einen großen Vorteil gegenüber uns – sie können die Thread-Klasse direkt entwickeln und ihr Felder und Operationen hinzufügen, und genau das haben sie getan.
Im java.lang.Thread
da sind folgende zeilen:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
Wie der Kommentar andeutet, handelt es sich tatsächlich um eine paketprivate Zuordnung aller Werte, die verfolgt werden ThreadLocal
Objekte dazu Thread
. Die Implementierung von ThreadLocalMap
ist kein WeakHashMap
aber es folgt dem gleichen Grundvertrag, einschließlich des Haltens seiner Schlüssel durch schwache Referenz.
ThreadLocal.get()
wird dann so implementiert:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
Und ThreadLocal.setInitialValue()
so:
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap if (map != null) map.set(this, value); else createMap(t, value); return value; }
Verwenden Sie im Wesentlichen eine Karte in diesem Thread alle unsere zu halten ThreadLocal
Objekte. Auf diese Weise müssen wir uns nie um die Werte in anderen Threads kümmern (ThreadLocal
können buchstäblich nur auf die Werte im aktuellen Thread zugreifen) und haben daher keine Parallelitätsprobleme. Außerdem einmal die Thread
fertig ist, wird seine Karte automatisch mit GC versehen und alle lokalen Objekte werden bereinigt. Auch wenn die Thread
festgehalten wird, die ThreadLocal
Objekte werden durch schwache Referenz gehalten und können aufgeräumt werden, sobald die ThreadLocal
Objekt verlässt den Gültigkeitsbereich.
Unnötig zu sagen, dass ich von dieser Implementierung ziemlich beeindruckt war, sie umgeht ziemlich elegant viele Nebenläufigkeitsprobleme (zugegebenermaßen, indem sie den Vorteil ausnutzt, Teil von Kern-Java zu sein, aber das ist verzeihlich, da es eine so clevere Klasse ist) und ermöglicht schnelles und Thread-sicherer Zugriff auf Objekte, auf die jeweils nur ein Thread zugreifen muss.
tl;dr ThreadLocal
Die Implementierung von ist ziemlich cool und viel schneller/intelligenter, als Sie auf den ersten Blick denken.
Wenn Ihnen diese Antwort gefallen hat, wissen Sie vielleicht auch meine (weniger detaillierte) Diskussion von zu schätzen ThreadLocalRandom
.
Thread
/ThreadLocal
Codeschnipsel entnommen aus Oracle/OpenJDKs Implementierung von Java 8.
-
Deine Antworten sehen toll aus, aber das ist mir jetzt zu lang. +1 und akzeptiert, und ich füge es meinem getpocket.com-Konto hinzu, um es später zu lesen. Vielen Dank!
– Reißer234
27. März 2013 um 11:59 Uhr
-
Ich brauche ein ThreadLocal-ähnliches Ding, mit dem ich auch auf die vollständige Liste der Werte zugreifen kann, ähnlich wie map.values(). Meine naive Implementierung ist also eine WeakHashMap
, wobei der Schlüssel Thread.currentThread().getName() ist. Dadurch wird der Verweis auf den Thread selbst vermieden. Wenn der Thread verschwindet, dann hält nichts mehr den Namen des Threads (eine Annahme, gebe ich zu) und mein Wert wird verschwinden. – bmauter
28. März 2013 um 14:50 Uhr
-
Nun, die schwachen Schlüssel sind nicht notwendig, aber erwägen Sie die Verwendung einer ConcurrentHashMap über einer synchronisierten HashMap – erstere ist für den Multithread-Zugriff konzipiert und funktioniert viel besser, wenn jeder Thread im Allgemeinen auf einen anderen Schlüssel zugreift.
– dimo414
28. März 2013 um 20:27 Uhr
-
@shmosel danke, froh, dass es informativ war! Ich glaube, es liegt daran
ThreadLocal
Die Anforderungen von unterscheiden sich geringfügig von denen von aWeakHashMap
. Zum Beispiel verwendet es a benutzerdefinierte Hash-Funktion “* das eliminiert Kollisionen im allgemeinen Fall, in dem nacheinander erstellte ThreadLocals von denselben Threads verwendet werden *”, und bietet unterschiedliche Bereinigungssemantik.– dimo414
12. Mai 2016 um 5:29 Uhr
-
@shmosel diese Klasse ist höchst abgestimmt, also würde ich davon ausgehen, dass solche Überlegungen angestellt wurden. Ein kurzer Blick zeigt das, wenn ein Thread normal beendet wird
Thread.exit()
wird aufgerufen und du wirst sehenthreadLocals = null;
genau da. Ein Kommentar verweist dieser Fehler die Sie vielleicht auch gerne lesen.– dimo414
12. Mai 2016 um 5:46 Uhr
Was meinen Sie java.lang.ThreadLocal
. Es ist eigentlich ganz einfach, es ist nur eine Karte von Name-Wert-Paaren, die in jedem gespeichert sind Thread
Objekt (siehe die Thread.threadLocals
aufstellen). Die API verbirgt dieses Implementierungsdetail, aber das ist mehr oder weniger alles, was dazu gehört.
-
Ich kann nicht sehen, warum welche benötigt werden sollten, da die Daten per Definition nur für einen einzelnen Thread sichtbar sind.
– Skaffmann
30. Juli 2009 um 7:14 Uhr
-
Richtig, es gibt keine Synchronisierung oder Sperrung um oder innerhalb der ThreadLocalMap, da nur im Thread darauf zugegriffen wird.
– Cowan
30. Juli 2009 um 7:31 Uhr
Chris Vest
ThreadLocal-Variablen in Java funktionieren durch den Zugriff auf eine HashMap, die von der Thread.currentThread()-Instanz gehalten wird.
-
Das ist nicht richtig (oder zumindest nicht mehr). Thread.currentThread() ist ein nativer Aufruf in der Thread.class. Außerdem hat Thread eine “ThreadLocalMap”, die ein einzelner Bucket (Array) von Hash-Codes ist. Dieses Objekt unterstützt die Map-Schnittstelle nicht.
– Benutzer924272
24. Juni 2014 um 16:59 Uhr
-
Das habe ich im Grunde gesagt. currentThread() gibt eine Thread-Instanz zurück, die eine Zuordnung von ThreadLocals zu Werten enthält.
– Chris Vest
25. Juni 2014 um 22:28 Uhr
Searene
Angenommen, Sie werden implementieren ThreadLocal
, wie machen Sie es Thread-spezifisch? Die einfachste Methode besteht natürlich darin, ein nicht statisches Feld in der Thread-Klasse zu erstellen, nennen wir es threadLocals
. Da jeder Thread durch eine Threadinstanz repräsentiert wird, also threadLocals
in jedem Thread wäre das auch anders. Und das macht Java auch:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
Was ist ThreadLocal.ThreadLocalMap
hier? Denn du hast nur eine threadLocals
für einen Thread, also wenn du einfach nimmst threadLocals
wie dein ThreadLocal
(z. B. definiere threadLocals als Integer
), haben Sie nur eine ThreadLocal
für einen bestimmten Faden. Was ist, wenn Sie mehrere wollen ThreadLocal
Variablen für einen Thread? Der einfachste Weg ist zu machen threadLocals
a HashMap
das key
von jedem Eintrag ist der Name des ThreadLocal
Variable, und die value
jedes Eintrags ist der Wert der ThreadLocal
Variable. Etwas verwirrend? Sagen wir, wir haben zwei Threads, t1
und t2
. sie nehmen das gleiche Runnable
Instanz als Parameter von Thread
Konstruktor, und beide haben zwei ThreadLocal
Variablen benannt tlA
und tlb
. So ist es.
t1.tlA
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 0 |
| tlB | 1 |
+-----+-------+
t2.tlB
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 2 |
| tlB | 3 |
+-----+-------+
Beachten Sie, dass die Werte von mir erstellt wurden.
Jetzt scheint es perfekt. Aber was ist ThreadLocal.ThreadLocalMap
? Warum hat es nicht einfach verwendet HashMap
? Um das Problem zu lösen, sehen wir uns an, was passiert, wenn wir einen Wert über die setzen set(T value)
Methode der ThreadLocal
Klasse:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
getMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Es schafft ein neues ThreadLocalMap
Instanz mit dem Strom ThreadLocal
Instanz und den einzustellenden Wert. Mal sehen, was ThreadLocalMap
ist wie, es ist in der Tat Teil der ThreadLocal
Klasse
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
...
}
Der Kernteil der ThreadLocalMap
Klasse ist die Entry class
die sich erstreckt WeakReference
. Es stellt sicher, dass beim Beenden des aktuellen Threads automatisch eine Garbage Collection durchgeführt wird. Aus diesem Grund verwendet es ThreadLocalMap
statt einfach HashMap
. Es leitet den Strom ThreadLocal
und seinen Wert als Parameter der Entry
Klasse, also wenn wir den Wert bekommen wollen, könnten wir ihn bekommen table
die eine Instanz von ist Entry
Klasse:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
So sieht das ganze Bild aus:
Konzeptionell können Sie an a denken ThreadLocal<T>
als Halten von a Map<Thread,T>
das die Thread-spezifischen Werte speichert, obwohl es nicht so implementiert ist.
Die Thread-spezifischen Werte werden im Thread-Objekt selbst gespeichert; Wenn der Thread beendet wird, können die Thread-spezifischen Werte einer Garbage Collection unterzogen werden.
Bezug : JCIP
-
Konzeptionell ja. Aber wie Sie aus anderen Antworten oben sehen, ist die Implementierung völlig anders.
– Architekt
2. Januar 2018 um 7:55 Uhr
-
Konzeptionell ja. Aber wie Sie aus anderen Antworten oben sehen, ist die Implementierung völlig anders.
– Architekt
2. Januar 2018 um 7:55 Uhr
12293900cookie-checkWie wird Javas ThreadLocal unter der Haube implementiert?