Wann und wie soll ich eine ThreadLocal-Variable verwenden?


876

Wann sollte ich eine ThreadLocalVariable verwenden?

Wie wird es benutzt?


11
Wenn Sie ThreadLocal verwenden, schreiben Sie niemals einen Wrapper darüber !!! Jeder Entwickler, der die Variable verwenden möchte, muss wissen, dass es sich um 'ThreadLocal' handelt
kein Fehler

2
@Notabug Wenn Sie explizit sind, können Sie Ihre Variable so benennen, dass es sich um einen ThreadLocal-Wert handelt, z RequestUtils.getCurrentRequestThreadLocal(). Das nicht zu sagen ist zwar sehr elegant, aber dies ergibt sich aus der Tatsache, dass ThreadLocal selbst in den meisten Fällen nicht sehr elegant ist.
Whirlwin

@ Sasha Kann der ThreadLocal verwendet werden, um Ausführungs-Trace zu speichern
Gaurav

Antworten:


864

Eine mögliche (und häufig verwendete) Verwendung ist, wenn Sie ein Objekt haben, das nicht threadsicher ist, aber Sie möchten vermeiden, den Zugriff auf dieses Objekt zu synchronisieren (ich sehe Sie an, SimpleDateFormat ). Geben Sie stattdessen jedem Thread eine eigene Instanz des Objekts.

Zum Beispiel:

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

Dokumentation .


160
Eine andere Alternative zu Synchronisation oder Threadlocal besteht darin, die Variable zu einer lokalen Variablen zu machen. Lokale Vars sind immer threadsicher. Ich gehe davon aus, dass es eine schlechte Praxis ist, DateFormats lokal zu machen, da ihre Erstellung teuer ist, aber ich habe zu diesem Thema noch nie solide Metriken gesehen.
Julien Chastang

18
Dies ist ein hoher Preis für das Hacken SimpleDateFormat. Vielleicht ist es besser, eine thread-sichere Alternative zu verwenden . Wenn Sie zustimmen Singletons sind schlecht , dann ThreadLocalist noch schlimmer.
Alexander Ryzhov

4
@overthink Gibt es einen Grund, ThreadLocal als statisch und endgültig zu deklarieren, ich meine Leistung oder so?
Sachin Gorade

4
Die ThreadLocal.get () -Methode ruft ThreadLocal.initialValue () (einmal) für jeden Thread auf. Dies bedeutet, dass für jeden Thread ein SimpleDateFormat-Objekt erstellt wird. Ist es nicht besser, SimpleDateFormat nur als lokale Variablen zu haben (da wir uns nicht mit Garbage Collection-Problemen befassen müssen)?
sudeepdino008

3
Achten Sie nur darauf, dass Sie für Ihr SimpleDateFormat keine doppelte Klammerinitialisierung verwenden, da dadurch eine anonyme Klasse erstellt wird, sodass Ihr Klassenladeprogramm nicht durch Müll gesammelt werden kann. Speicherverlust: neues SimpleDateFormat () zurückgeben {{applyPattern ("yyyyMMdd HHmm")}};
user1944408

427

Da a ThreadLocalein Verweis auf Daten innerhalb eines bestimmten ThreadBereichs ist, kann es bei der Verwendung von ThreadLocals auf Anwendungsservern mit Thread-Pools zu Klassenladeverlusten kommen . Sie müssen sehr vorsichtig sein , keine Reinigung ThreadLocals Sie get()oder set()durch die Verwendung von ThreadLocal‚s - remove()Methode.

Wenn Sie nach Abschluss nicht bereinigen, verbleiben alle Verweise auf Klassen, die als Teil einer bereitgestellten Webanwendung geladen wurden, auf dem permanenten Heap und werden niemals mit Müll gesammelt. Durch das erneute Bereitstellen / Aufheben der Bereitstellung der Webanwendung werden nicht alle ThreadVerweise auf die Klassen Ihrer Webanwendung bereinigt, da diese Threadnicht Eigentum Ihrer Webanwendung sind. Bei jeder aufeinanderfolgenden Bereitstellung wird eine neue Instanz der Klasse erstellt, die niemals durch Müll gesammelt wird.

Sie werden aufgrund von java.lang.OutOfMemoryError: PermGen spaceund nach einigem googeln wahrscheinlich nur wenige Speicherausnahmen haben, -XX:MaxPermSizeanstatt den Fehler zu beheben.

Wenn diese Probleme auftreten, können Sie mithilfe des Memory Analyzer von Eclipse und / oder anhand der Anleitung und des Follow-up von Frank Kieviet feststellen, in welchem ​​Thread und in welcher Klasse diese Referenzen gespeichert sind .

Update: Alex Vasseurs Blogeintrag wurde neu entdeckt , der mir dabei half, einige ThreadLocalProbleme aufzuspüren , die ich hatte.


11
Alex Vasseur hat seinen Blog verschoben. Hier ist ein aktueller Link zum Artikel über Speicherverluste.
Kenster

12
Der Thread, auf den Julien verlinkt, ist anscheinend hierher gezogen und es lohnt sich zu lesen…
Donal Fellows

28
Dies ist eine Menge Gegenstimmen für eine Antwort, die zwar informativ ist, die Frage aber in keiner Weise tatsächlich beantwortet.
Robin

8
Ändert dies nun, da PermGen von Java 8 getötet wird, diese Antwort in irgendeiner Weise?
Böse Waschmaschine

13
@ Robin: Ich bin anderer Meinung. Die Frage ist, wie man ThreadLocal richtig verwendet, um ein vollständiges Verständnis eines Konzepts zu erhalten (ThreadLocal hier). Es ist auch wichtig zu verstehen, wie man es nicht verwendet und welche Risiken es hat, wenn man es unachtsam verwendet, und darum geht es in Phils Antwort. Ich bin froh, dass er diesen Punkt behandelt hat, der in keiner anderen Antwort behandelt wird. Alle diese Stimmen sind verdient. Ich glaube, SO sollte das Verständnis für Konzepte fördern, anstatt nur eine QS-Site zu sein.
Saurabh Patil

166

Viele Frameworks verwenden ThreadLocals, um einen Kontext für den aktuellen Thread beizubehalten. Wenn die aktuelle Transaktion beispielsweise in einem ThreadLocal gespeichert ist, müssen Sie sie nicht bei jedem Methodenaufruf als Parameter übergeben, falls jemand im Stack Zugriff darauf benötigt. Webanwendungen speichern möglicherweise Informationen über die aktuelle Anforderung und Sitzung in einem ThreadLocal, sodass die Anwendung einfachen Zugriff darauf hat. Mit Guice können Sie ThreadLocals verwenden, wenn Sie benutzerdefinierte Bereiche für die injizierten Objekte implementieren (die Standard- Servlet-Bereiche von Guice verwenden sie höchstwahrscheinlich auch).

ThreadLocals sind eine Art globaler Variablen (obwohl etwas weniger böse, da sie auf einen Thread beschränkt sind). Sie sollten daher vorsichtig sein, wenn Sie sie verwenden, um unerwünschte Nebenwirkungen und Speicherverluste zu vermeiden. Entwerfen Sie Ihre APIs so, dass die ThreadLocal-Werte immer dann automatisch gelöscht werden, wenn sie nicht mehr benötigt werden und eine falsche Verwendung der API nicht möglich ist (z. B. so ). ThreadLocals können verwendet werden, um den Code sauberer zu machen, und in einigen seltenen Fällen sind sie die einzige Möglichkeit, etwas zum Laufen zu bringen (mein aktuelles Projekt hatte zwei solcher Fälle; sie sind hier unter "Statische Felder und globale Variablen" dokumentiert ).


4
Genau so ermöglicht das exPOJO-Framework (www.expojo.com) den Zugriff auf ORM Session / PersistenceManager, ohne dass der Aufwand für Anmerkungen und Injektionen erforderlich ist. Es ist eine Art "Thread-Injection" anstelle von "Object Injection". Es bietet Zugriff auf Abhängigkeiten, ohne dass diese in jedes Objekt eingebettet werden müssen, das diese Abhängigkeiten möglicherweise benötigt. Es ist erstaunlich, wie 'leicht' Sie ein DI-Framework erstellen können, wenn Sie anstelle der klassischen DI (z. B. Feder usw.) eine
Fadeninjektion verwenden

27
Warum musste ich so weit nach unten scrollen, um diese wichtige Antwort zu finden?
Jeremy Stein

1
@Esko, In Ihrem Projekt ist Ihre Verwendung von ThreadLocal in Hashcode und gleich nicht erforderlich. Ein besserer Ansatz besteht darin, bei Bedarf einen Verweis auf eine Hashcode / Equals-Funktion zu erstellen oder zu übergeben. Auf diese Weise können Sie die Notwendigkeit eines ThreadLocal-Hacks vollständig vermeiden.
Pacerier


1
Dies ist die beste Erklärung für die Verwendung von TL.
Dexter

52

Wenn Sie in Java ein Datum haben, das pro Thread variieren kann, können Sie dieses Datum an jede Methode weitergeben, die es benötigt (oder möglicherweise benötigt), oder das Datum dem Thread zuordnen. Das Übergeben des Datums überall kann funktionieren, wenn alle Ihre Methoden bereits eine gemeinsame "Kontext" -Variable weitergeben müssen.

Wenn dies nicht der Fall ist, möchten Sie Ihre Methodensignaturen möglicherweise nicht mit einem zusätzlichen Parameter überladen. In einer Welt ohne Thread können Sie das Problem mit dem Java-Äquivalent einer globalen Variablen lösen. In einem Thread-Wort ist das Äquivalent einer globalen Variablen eine thread-lokale Variable.


13
Sie sollten Thread-Locals genauso vermeiden wie Globals. Ich kann unmöglich akzeptieren, dass es in Ordnung ist, Globals (Thread-Locals) zu erstellen, anstatt Werte weiterzugeben. Die Leute mögen das nicht, weil es häufig Architekturprobleme aufdeckt, die sie nicht beheben möchten.
Juan Mendes

10
Vielleicht ... Es kann jedoch nützlich sein, wenn Sie über eine große, vorhandene Codebasis verfügen, zu der Sie ein neues Datum hinzufügen müssen, das überall weitergegeben werden muss , z. B. einen Sitzungskontext, eine Datenbanktransaktion, einen angemeldeten Benutzer usw. .
Cornel Masson

6
Ein großes Lob für die Verwendung von Datum anstelle von Daten.
Tief

Das hat mich neugierig gemacht. 'datum' is the singular form and 'data' is the plural form.
Siddhartha vor

23

Es gibt ein sehr gutes Beispiel in Buch Java Concurrency in Practice . Wo der Autor ( Joshua Bloch ) erklärt, wie die Thread-Beschränkung eine der einfachsten Möglichkeiten ist, um die Thread-Sicherheit zu erreichen, und ThreadLocal ein formelleres Mittel zur Aufrechterhaltung der Thread-Beschränkung ist. Am Ende erklärt er auch, wie Menschen es missbrauchen können, indem sie es als globale Variablen verwenden.

Ich habe den Text aus dem erwähnten Buch kopiert, aber Code 3.10 fehlt, da es nicht sehr wichtig ist zu verstehen, wo ThreadLocal verwendet werden soll.

Thread-lokale Variablen werden häufig verwendet, um die gemeinsame Nutzung in Designs zu verhindern, die auf veränderlichen Singletons oder globalen Variablen basieren. Beispielsweise kann eine Single-Threaded-Anwendung eine globale Datenbankverbindung aufrechterhalten, die beim Start initialisiert wird, um zu vermeiden, dass an jede Methode eine Verbindung übergeben werden muss. Da JDBC-Verbindungen möglicherweise nicht threadsicher sind, ist eine Multithread-Anwendung, die eine globale Verbindung ohne zusätzliche Koordination verwendet, auch nicht threadsicher. Durch die Verwendung eines ThreadLocal zum Speichern der JDBC-Verbindung, wie in ConnectionHolder in Listing 3.10, verfügt jeder Thread über eine eigene Verbindung.

ThreadLocal wird häufig bei der Implementierung von Anwendungsframeworks verwendet. Beispielsweise ordnen J2EE-Container einem ausführenden Thread für die Dauer eines EJB-Aufrufs einen Transaktionskontext zu. Dies kann einfach mithilfe eines statischen Thread-Local implementiert werden, der den Transaktionskontext enthält: Wenn der Framework-Code bestimmen muss, welche Transaktion gerade ausgeführt wird, wird der Transaktionskontext aus diesem ThreadLocal abgerufen. Dies ist insofern praktisch, als es die Notwendigkeit verringert, Ausführungskontextinformationen an jede Methode zu übergeben, aber jeden Code, der diesen Mechanismus verwendet, an das Framework koppelt.

Es ist leicht, ThreadLocal zu missbrauchen, indem seine Thread-Confinement-Eigenschaft als Lizenz zur Verwendung globaler Variablen oder als Mittel zum Erstellen von "versteckten" Methodenargumenten behandelt wird. Wie globale Variablen können threadlokale Variablen die Wiederverwendbarkeit beeinträchtigen und versteckte Kopplungen zwischen Klassen einführen. Sie sollten daher mit Vorsicht verwendet werden.


18

Wenn der Wert einer Variablen vom aktuellen Thread abhängt und es für Sie nicht bequem ist, den Wert auf andere Weise an den Thread anzuhängen (z. B. Unterklassen).

Ein typischer Fall ist, wenn ein anderes Framework den Thread erstellt hat , in dem Ihr Code ausgeführt wird, z. B. ein Servlet-Container, oder wenn die Verwendung von ThreadLocal nur sinnvoller ist, weil sich Ihre Variable dann "an ihrer logischen Stelle" befindet (und nicht an einer Variablen) an einer Thread-Unterklasse oder in einer anderen Hash-Map hängen).

Auf meiner Website habe ich einige weitere Diskussionen und Beispiele für die Verwendung von ThreadLocal , die ebenfalls von Interesse sein können.

Einige Leute befürworten die Verwendung von ThreadLocal, um jedem Thread in bestimmten gleichzeitigen Algorithmen, in denen Sie eine Thread-Nummer benötigen, eine "Thread-ID" zuzuweisen (siehe z. B. Herlihy & Shavit). Überprüfen Sie in solchen Fällen, ob Sie wirklich einen Vorteil erhalten!


Kann der ThreadLocal zum Speichern der Ausführungsablaufverfolgung verwendet werden?
gaurav

13
  1. ThreadLocal in Java wurde in JDK 1.2 eingeführt, später jedoch in JDK 1.5 generiert, um die Typensicherheit für die ThreadLocal-Variable einzuführen.

  2. ThreadLocal kann dem Thread-Bereich zugeordnet werden. Der gesamte von Thread ausgeführte Code hat Zugriff auf ThreadLocal-Variablen, aber zwei Threads können die ThreadLocal-Variablen des jeweils anderen nicht sehen.

  3. Jeder Thread enthält eine exklusive Kopie der ThreadLocal-Variablen, die für die Garbage Collection berechtigt ist, nachdem der Thread beendet wurde oder gestorben ist, normalerweise oder aufgrund einer Ausnahme, vorausgesetzt, diese ThreadLocal-Variablen haben keine anderen Live-Referenzen.

  4. ThreadLocal-Variablen in Java sind im Allgemeinen private statische Felder in Klassen und behalten ihren Status innerhalb von Thread bei.

Lesen Sie mehr: ThreadLocal in Java - Beispielprogramm und Tutorial


Kann der ThreadLocal verwendet werden, um die Ausführungsverfolgung zu speichern
gaurav

11

Die Dokumentation heißt es sehr gut: "Jeder Thread, der (über seine get- oder set-Methode) auf [eine threadlokale Variable] zugreift, hat eine eigene, unabhängig initialisierte Kopie der Variablen."

Sie verwenden eine, wenn jeder Thread eine eigene Kopie von etwas haben muss. Standardmäßig werden Daten zwischen Threads geteilt.


18
Standardmäßig werden statische Objekte oder Objekte, die explizit zwischen Threads übergeben werden, wobei beide Threads einen Verweis auf dasselbe Objekt besitzen, gemeinsam genutzt. In einem Thread lokal deklarierte Objekte werden nicht gemeinsam genutzt (sie befinden sich lokal im Stapel des Threads). Ich wollte das nur klarstellen.
Ian Varley

@ IanVarley: Danke, dass du darauf hingewiesen hast. Wenn wir also eine Variable haben, die in der MyThread-Klasse deklariert und verwendet wird, brauchen wir dann ThreadLocal in diesem Fall? Beispiel: Klasse MyThread erweitert Thread {String var1;, int var2; } In diesem Fall sind var1 und var2 Teil des Thread-eigenen Stacks und werden nicht gemeinsam genutzt. Benötigen wir in diesem Fall ThreadLocal? Ich könnte in meinem Verständnis völlig falsch liegen, bitte führen.
Jayesh

4
Wenn jeder Thread eine eigene Kopie von etwas haben möchte, warum kann er dann nicht einfach als lokal deklariert werden (was immer threadsicher ist)?
sudeepdino008

@ sudeepdino008 Sie haben nicht immer die Kontrolle über die Erstellung dieser Threads (Webapp-Frameworks, Threadpool-Bibliotheken)
JMess

10

Der Webapp-Server behält möglicherweise einen Thread-Pool bei, und eine ThreadLocalVariable sollte vor der Antwort an den Client entfernt werden. Daher kann der aktuelle Thread bei der nächsten Anforderung wiederverwendet werden.


8

Zwei Anwendungsfälle, in denen eine threadlokale Variable verwendet werden kann:
1- Wenn der Status einem Thread zugeordnet werden muss (z. B. eine Benutzer-ID oder eine Transaktions-ID). Dies geschieht normalerweise bei einer Webanwendung, der jeder Anforderung, die an ein Servlet gesendet wird, eine eindeutige Transaktions-ID zugeordnet ist.

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

Beachten Sie, dass hier die Methode withInitial mithilfe des Lambda-Ausdrucks implementiert wird.
2- Ein weiterer Anwendungsfall ist, wenn wir eine thread-sichere Instanz haben möchten und keine Synchronisation verwenden möchten, da die Leistungskosten bei der Synchronisation höher sind. Ein solcher Fall ist, wenn SimpleDateFormat verwendet wird. Da SimpleDateFormat nicht threadsicher ist, müssen wir einen Mechanismus bereitstellen, um es threadsicher zu machen.

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

}

Ich habe immer noch nicht den Vorteil gesehen, ThreadLocal zum Generieren der eindeutigen ID in Beispiel 1 zu verwenden, wenn Sie nur einen inkrementellen eindeutigen ganzzahligen Wert zurückgeben möchten. `` `öffentliche Klasse ThreadId {// Atomic Integer, die die nächste Thread-ID enthält, der eine private statische endgültige AtomicInteger zugewiesen werden soll nextId = new AtomicInteger (0); // Gibt die eindeutige ID des aktuellen Threads zurück und weist sie bei Bedarf zu. Public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen

7

Seit der Veröffentlichung von Java 8 gibt es eine deklarativere Möglichkeit zum Initialisieren ThreadLocal:

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

Bis zur Veröffentlichung von Java 8 mussten Sie Folgendes tun:

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

Wenn die Instanziierungsmethode (Konstruktor, Factory-Methode) der Klasse, für ThreadLocaldie verwendet wird, keine Parameter akzeptiert, können Sie einfach Methodenreferenzen verwenden (eingeführt in Java 8):

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

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

Hinweis: Die Auswertung ist verzögert, da Sie java.util.function.SupplierLambda übergeben, das nur ausgewertet wird, wenn ThreadLocal#getes aufgerufen wird, der Wert jedoch zuvor nicht ausgewertet wurde.


5

Sie müssen mit dem ThreadLocal-Muster sehr vorsichtig sein. Es gibt einige wichtige Nachteile, wie Phil erwähnt hat, aber eine, die nicht erwähnt wurde, ist sicherzustellen, dass der Code, der den ThreadLocal-Kontext einrichtet, nicht "neu eintritt".

Schlechte Dinge können passieren, wenn der Code, der die Informationen festlegt, ein zweites oder drittes Mal ausgeführt wird, da Informationen in Ihrem Thread mutieren können, wenn Sie dies nicht erwartet haben. Stellen Sie daher sicher, dass die ThreadLocal-Informationen nicht festgelegt wurden, bevor Sie sie erneut festlegen.


2
Der Wiedereintritt ist kein Problem, wenn der Code darauf vorbereitet ist. Notieren Sie sich bei der Eingabe, ob die Variable bereits festgelegt ist, und stellen Sie beim Beenden den vorherigen Wert wieder her (falls vorhanden) oder entfernen Sie ihn (falls nicht).
Supercat

1
@ Jeff, Alter, das gilt für jeden Code, den du schreibst, nicht nur für ThreadLocalMuster. Wenn Sie dies tun F(){ member=random(); F2(); write(member); }und F2 das Mitglied mit einem neuen Wert überschreibt, write(member)wird die von Ihnen random()eingegebene Nummer offensichtlich nicht mehr geschrieben . Dies ist buchstäblich nur gesunder Menschenverstand. Ebenso, wenn Sie dies tun F(){ F(); }, dann viel Glück mit Ihrer Endlosschleife! Dies gilt überall und ist nicht spezifisch für ThreadLocal.
Pacerier

@ Jeff, Codebeispiel?
Pacerier

5

wann?

Wenn ein Objekt nicht threadsicher ist, geben Sie anstelle einer Synchronisierung, die die Skalierbarkeit beeinträchtigt, jedem Thread ein Objekt und behalten Sie den Thread-Bereich bei, nämlich ThreadLocal. Eines der am häufigsten verwendeten, aber nicht threadsicheren Objekte sind Datenbankverbindung und JMSConnection.

Wie ?

Ein Beispiel ist, dass das Spring Framework ThreadLocal in hohem Maße zum Verwalten von Transaktionen hinter den Kulissen verwendet, indem diese Verbindungsobjekte in ThreadLocal-Variablen beibehalten werden. Auf hoher Ebene erhält eine Transaktion beim Starten die Verbindung (und deaktiviert das automatische Festschreiben) und behält sie in ThreadLocal bei. Bei weiteren Datenbankaufrufen wird dieselbe Verbindung für die Kommunikation mit der Datenbank verwendet. Am Ende wird die Verbindung von ThreadLocal übernommen, die Transaktion festgeschrieben (oder zurückgesetzt) ​​und die Verbindung freigegeben.

Ich denke, log4j verwendet ThreadLocal auch zur Wartung von MDC.


5

ThreadLocal Dies ist nützlich, wenn Sie einen Status haben möchten, der nicht von verschiedenen Threads gemeinsam genutzt werden soll, der jedoch während seiner gesamten Lebensdauer von jedem Thread aus zugänglich sein sollte.

Stellen Sie sich als Beispiel eine Webanwendung vor, bei der jede Anforderung von einem anderen Thread bedient wird. Stellen Sie sich vor, Sie benötigen für jede Anforderung mehrmals Daten, was für die Berechnung recht teuer ist. Diese Daten haben sich jedoch möglicherweise für jede eingehende Anforderung geändert. Dies bedeutet, dass Sie keinen einfachen Cache verwenden können. Eine einfache und schnelle Lösung für dieses Problem wäre eine ThreadLocalVariable, die Zugriff auf diese Daten hat, sodass Sie sie für jede Anforderung nur einmal berechnen müssen. Natürlich kann dieses Problem auch ohne die Verwendung von gelöst werden ThreadLocal, aber ich habe es zu Illustrationszwecken entwickelt.

Denken Sie jedoch daran, dass ThreadLocals im Wesentlichen eine Form des globalen Staates sind. Infolgedessen hat es viele andere Auswirkungen und sollte erst verwendet werden, nachdem alle anderen möglichen Lösungen berücksichtigt wurden.


ThreadLocals sind KEIN globaler Status, es sei denn, Sie machen ihn zum globalen Status. Sie sind praktisch immer zugängliche Stapelressourcen. Sie können den lokalen Thread simulieren, indem Sie diese Variable einfach in all Ihren Methoden übergeben (was verzögert ist). das macht es nicht globaler Zustand ...
Enerccio

Es ist eine Form des globalen Status in dem Sinne, dass von überall im Code (im Kontext desselben Threads) darauf zugegriffen werden kann. Dies hat alle Auswirkungen, z. B. dass Sie nicht darüber nachdenken können, wer diesen Wert liest und schreibt. Die Verwendung eines Funktionsparameters ist keine Verzögerung, sondern eine bewährte Methode zur Förderung sauberer Schnittstellen. Ich stimme Ihnen jedoch zu, dass das Übergeben eines Parameters über die gesamte Tiefe Ihrer Codebasis ein Codegeruch ist. Aber ich glaube auch, dass die Verwendung von ThreadLocal in vielen Fällen in erster Linie ein Codegeruch ist, der Sie hierher geführt hat, also sollte man dies überdenken.
Dimos

Es kann einfach Teil des Objektstatus sein und muss nicht global verwendet werden. Sicher, Sie bekommen ein wenig Overhead, aber wenn mehrere Objekte einen unterschiedlichen Status pro Thread haben müssen, können Sie ThreadLocalals Objektfeld verwenden ...
Enerccio

Du hast recht. Ich bin wahrscheinlich auf mehrere Missbräuche gestoßen ThreadLocal, bei denen es weltweit zugänglich war. Wie Sie sagten, kann es weiterhin als Klassenfeld verwendet werden, das die Sichtbarkeit einschränkt.
Dimos

4

Hier gibt es nichts wirklich Neues, aber ich habe heute festgestellt, dass dies ThreadLocalsehr nützlich ist, wenn Bean Validation in einer Webanwendung verwendet wird. Validierungsnachrichten sind lokalisiert, werden jedoch standardmäßig verwendet Locale.getDefault(). Sie können das Validatormit einem anderen konfigurieren MessageInterpolator, aber es gibt keine Möglichkeit, das anzugeben, Localewenn Sie anrufen validate. Sie können also einen statischen ThreadLocal<Locale>(oder besser noch einen allgemeinen Container mit anderen Dingen erstellen , die Sie möglicherweise benötigen, ThreadLocalund dann Ihren benutzerdefinierten Container MessageInterpolatorauswählen lassen Locale. Der nächste Schritt besteht darin, einen zu schreiben, ServletFilterder einen Sitzungswert verwendet, oder request.getLocale()das Gebietsschema auszuwählen und zu speichern es in Ihrer ThreadLocalReferenz.


4

Wie von @unknown (google) erwähnt, wird eine globale Variable definiert, in der der referenzierte Wert in jedem Thread eindeutig sein kann. Bei der Verwendung werden normalerweise Kontextinformationen gespeichert, die mit dem aktuellen Ausführungsthread verknüpft sind.

Wir verwenden es in einer Java EE-Umgebung, um die Benutzeridentität an Klassen zu übergeben, die nicht Java EE-fähig sind (keinen Zugriff auf HttpSession oder den EJB SessionContext). Auf diese Weise kann der Code, der die Identität für sicherheitsbasierte Vorgänge verwendet, von überall auf die Identität zugreifen, ohne sie bei jedem Methodenaufruf explizit übergeben zu müssen.

Der Anforderungs- / Antwortzyklus von Vorgängen in den meisten Java EE-Aufrufen erleichtert diese Art der Verwendung, da genau definierte Ein- und Ausstiegspunkte zum Festlegen und Deaktivieren des ThreadLocal bereitgestellt werden.


4

ThreadLocal stellt sicher, dass der Zugriff auf das veränderbare Objekt durch die mehreren Threads in der nicht synchronisierten Methode synchronisiert wird. Dies bedeutet, dass das veränderbare Objekt innerhalb der Methode unveränderlich ist.

Dies wird erreicht, indem für jeden Thread, der versucht, darauf zuzugreifen, eine neue Instanz eines veränderlichen Objekts angegeben wird. Es ist also eine lokale Kopie für jeden Thread. Dies ist ein Hack, um Instanzvariablen in einer Methode, auf die zugegriffen werden soll, wie eine lokale Variable zu machen. Wie Sie wissen, ist die lokale Variable der Methode nur für den Thread verfügbar. Ein Unterschied besteht darin, Lokale Methodenvariablen stehen dem Thread nicht zur Verfügung, sobald die Methodenausführung abgeschlossen ist. Da das mit threadlocal gemeinsam genutzte veränderbare Objekt für mehrere Methoden verfügbar ist, bis wir es bereinigen.

Per Definition:

Mit der ThreadLocal-Klasse in Java können Sie Variablen erstellen, die nur von demselben Thread gelesen und geschrieben werden können. Selbst wenn zwei Threads denselben Code ausführen und der Code auf eine ThreadLocal-Variable verweist, können die beiden Threads die ThreadLocal-Variablen des anderen nicht sehen.

Jeder Threadin Java enthält ThreadLocalMapdarin.
Wo

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

Erreichen des ThreadLocal:

Erstellen Sie nun eine Wrapper-Klasse für ThreadLocal, die das veränderbare Objekt wie unten (mit oder ohne initialValue()) enthält.
Jetzt arbeiten Getter und Setter dieses Wrappers mit einer threadlokalen Instanz anstelle eines veränderlichen Objekts.

Wenn getter () von threadlocal keinen Wert mit in der threadlocalmap von gefunden hat Thread; dann ruft es den initialValue () auf, um seine private Kopie in Bezug auf den Thread zu erhalten.

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

Nun wrapper.getDateFormatter()rufen threadlocal.get()und prüft das currentThread.threadLocalMapenthält diese (Thread) Instanz.
Wenn ja, geben Sie den Wert (SimpleDateFormat) für die entsprechende threadlokale Instanz zurück,
andernfalls fügen Sie die Zuordnung mit dieser threadlokalen Instanz, initialValue (), hinzu.

Hiermit wurde die Fadensicherheit für diese veränderliche Klasse erreicht; Jeder Thread arbeitet mit seiner eigenen veränderlichen Instanz, jedoch mit derselben ThreadLocal-Instanz. Bedeutet, dass alle Threads dieselbe ThreadLocal-Instanz als Schlüssel verwenden, jedoch eine andere SimpleDateFormat-Instanz als Wert.

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


3

Thread-lokale Variablen werden häufig verwendet, um die gemeinsame Nutzung in Designs zu verhindern, die auf veränderlichen Singletons oder globalen Variablen basieren.

Es kann in Szenarien wie dem Herstellen einer separaten JDBC-Verbindung für jeden Thread verwendet werden, wenn Sie keinen Verbindungspool verwenden.

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

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

Wenn Sie getConnection aufrufen, wird eine Verbindung zurückgegeben, die diesem Thread zugeordnet ist. Dasselbe kann mit anderen Eigenschaften wie Datumsformat und Transaktionskontext durchgeführt werden, die Sie nicht zwischen Threads teilen möchten.

Sie hätten auch lokale Variablen verwenden können, aber diese Ressourcen benötigen normalerweise Zeit für die Erstellung, sodass Sie sie nicht immer wieder erstellen möchten, wenn Sie eine Geschäftslogik mit ihnen ausführen. ThreadLocal-Werte werden jedoch im Thread-Objekt selbst gespeichert, und sobald der Thread durch Müll gesammelt wird, sind auch diese Werte verschwunden.

Dieser Link erklärt die Verwendung von ThreadLocal sehr gut.


2
In diesem Beispiel ist ein wichtiges Problem: Wer ist für das Schließen der Verbindung verantwortlich? Anstatt diese Verbindung als Initiali-Wert zu erstellen, lassen Sie den Konsumenten der Verbindung die Verbindung explizit erstellen und über ThreadLocal an den Thread binden. Der Ersteller der Verbindung ist dann auch für das Schließen verantwortlich. Dies ist in diesem Beispiel nicht klar. Das Erstellen und Binden der Verbindung kann auch in einem einfachen Framework durch Transaction.begin () und Transaction.end () ausgeblendet werden.
Rick-Rainer Ludwig

2

Mit der ThreadLocal- Klasse in Java können Sie Variablen erstellen, die nur von demselben Thread gelesen und geschrieben werden können. Selbst wenn zwei Threads denselben Code ausführen und der Code einen Verweis auf eine ThreadLocal-Variable enthält, können die beiden Threads die ThreadLocal-Variablen des anderen nicht sehen.

Weiterlesen


2

Es gibt drei Szenarien für die Verwendung eines Klassenhelfers wie SimpleDateFormat in Multithread-Code. Am besten verwenden Sie ThreadLocal

Szenarien

1- Verwenden eines ähnlichen Freigabeobjekts mithilfe eines Sperr- oder Synchronisationsmechanismus , der die App verlangsamt

2- Verwendung als lokales Objekt innerhalb einer Methode

Wenn in diesem Szenario 4 Threads jeweils 1000 Mal eine Methode aufrufen, haben wir
4000 SimpleDateFormat- Objekte erstellt und warten darauf, dass GC sie löscht

3- Verwenden von ThreadLocal

Wenn wir 4 Threads haben und jedem Thread eine SimpleDateFormat-Instanz gegeben
haben, haben wir 4 Threads , 4 Objekte von SimpleDateFormat.

Es ist kein Sperrmechanismus sowie keine Objekterstellung und -zerstörung erforderlich. (Gute Zeitkomplexität und Raumkomplexität)


2

[Als Referenz] ThreadLocal kann Aktualisierungsprobleme von gemeinsam genutzten Objekten nicht lösen. Es wird empfohlen, ein staticThreadLocal-Objekt zu verwenden, das von allen Vorgängen im selben Thread gemeinsam genutzt wird. Die Methode [Obligatorisch] remove () muss von ThreadLocal-Variablen implementiert werden, insbesondere wenn Thread-Pools verwendet werden, in denen Threads häufig wiederverwendet werden. Andernfalls kann dies die nachfolgende Geschäftslogik beeinträchtigen und unerwartete Probleme wie Speicherverluste verursachen.


1

Caching, manchmal müssen Sie den gleichen Wert viel Zeit berechnen, damit Sie den Code beschleunigen können, indem Sie den letzten Satz von Eingaben in einer Methode und das Ergebnis speichern. Durch die Verwendung von Thread Local Storage müssen Sie nicht über das Sperren nachdenken.


1

ThreadLocal ist eine von JVM speziell bereitgestellte Funktionalität, um einen isolierten Speicherplatz nur für Threads bereitzustellen. Ebenso wie der Wert einer Variablen mit Instanzbereich nur an eine bestimmte Instanz einer Klasse gebunden ist. Jedes Objekt hat seine einzigen Werte und sie können sich nicht gegenseitig sehen. So ist das Konzept der ThreadLocal-Variablen, sie sind lokal für den Thread im Sinne von Objektinstanzen, die andere Threads außer demjenigen, der sie erstellt hat, nicht sehen können. Siehe hier

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 bietet eine sehr einfache Möglichkeit, die Wiederverwendbarkeit von Objekten ohne Kosten zu erreichen.

Ich hatte eine Situation, in der mehrere Threads bei jeder Update-Benachrichtigung ein Image des veränderlichen Caches erstellten.

Ich habe für jeden Thread ein Threadlocal verwendet, und dann müsste jeder Thread nur das alte Image zurücksetzen und es bei jeder Update-Benachrichtigung erneut aus dem Cache aktualisieren.

Übliche wiederverwendbare Objekte aus Objektpools sind mit Thread-Sicherheitskosten verbunden, während dieser Ansatz keine Kosten hat.


0

Probieren Sie dieses kleine Beispiel aus, um ein Gefühl für die ThreadLocal-Variable zu bekommen:

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


Konsolenausgabe, wenn Thread BookA zuerst ausgeführt wird:
Ergebnis BookA: 'wordA1, wordA2, wordA3'.
ErgebnisbuchB: 'WortB1, WortB2'.

Konsolenausgabe, wenn Thread BookB zuerst ausgeführt wird:
Ergebnis BookB: 'wordB1, wordB2'.
ErgebnisbuchA: 'WortA1, WortA2, WortA3'.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.