Jeśli używasz ThreadLocal, nigdy nie pisz na nim opakowania !!! Każdy programista, który chce korzystać ze zmiennej, musi wiedzieć, że jest to „ThreadLocal”
nie jest to błąd
2
@Notabug Jeśli wyrażasz się jasno, możesz nazwać swoją zmienną tak, aby to ona zajmowała się wartością ThreadLocal, np RequestUtils.getCurrentRequestThreadLocal(). Nie oznacza to jednak, że jest to bardzo eleganckie, ale wynika to z faktu, że sam ThreadLocal w większości przypadków nie jest zbyt elegancki.
Whirlwin
@Sasha Can ThreadLocal może być używany do przechowywania śladu wykonania
gaurav
Odpowiedzi:
864
Jednym z możliwych (i powszechnych) zastosowań jest posiadanie obiektu, który nie jest bezpieczny dla wątków, ale chcesz uniknąć synchronizowania dostępu do tego obiektu (patrzę na ciebie, SimpleDateFormat ). Zamiast tego nadaj każdemu wątkowi własną instancję obiektu.
Na przykład:
publicclassFoo{// SimpleDateFormat is not thread-safe, so give one to each threadprivatestaticfinalThreadLocal<SimpleDateFormat> formatter =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){returnnewSimpleDateFormat("yyyyMMdd HHmm");}};publicString formatIt(Date date){return formatter.get().format(date);}}
Inną alternatywą dla synchronizacji lub Threadlocal jest uczynienie zmiennej zmienną lokalną. Lokalne zmienne są zawsze bezpieczne dla wątków. Zakładam, że złym rozwiązaniem jest tworzenie lokalnych formatów DateFormats, ponieważ ich tworzenie jest kosztowne, ale nigdy nie widziałem solidnych danych na ten temat.
@ overthink Czy istnieje powód, aby zadeklarować statyczny i końcowy ThreadLocal, mam na myśli wydajność lub coś takiego?
Sachin Gorade,
4
Metoda ThreadLocal.get () wywoła ThreadLocal.initialValue () (jeden raz) dla każdego wątku, co oznacza, że dla każdego wątku tworzony jest obiekt SimpleDateFormat. Czy nie lepiej jest po prostu mieć SimpleDateFormat jako zmienne lokalne (ponieważ nie mamy do czynienia z problemami związanymi z odśmiecaniem)?
sudeepdino008
3
Uważaj tylko, aby nie używać inicjalizacji podwójnego nawiasu klamrowego dla Twojego SimpleDateFormat, ponieważ stworzyłoby to anonimową klasę, dzięki czemu moduł ładujący klasy nie mógłby zostać wyrzucony. wyciek pamięci: zwraca nowy SimpleDateFormat () {{ApplyPattern ("rrrrMMdd GGmm")}};
user1944408 27.07.16
427
Ponieważ a ThreadLocaljest odniesieniem do danych w danym obszarze Thread, możesz skończyć przeciekami klasy podczas używania ThreadLocals na serwerach aplikacji za pomocą pul wątków. Trzeba być bardzo ostrożnym czyszczenia żadnych ThreadLocaljesteś ty get()lub set()za pomocą ThreadLocal„s remove()metody.
Jeśli nie wyczyścisz po zakończeniu, wszelkie odwołania do klas załadowanych jako część wdrożonej aplikacji internetowej pozostaną na stałym stosie i nigdy nie zostaną usunięte. Ponowne wdrożenie / cofnięcie aplikacji internetowej nie wyczyści Threadodniesienia do klas aplikacji, ponieważ Threadnie jest to własność aplikacji internetowej. Każde kolejne wdrożenie spowoduje utworzenie nowej instancji klasy, która nigdy nie będzie zbierana w pamięci.
java.lang.OutOfMemoryError: PermGen spaceSkończysz z brakami pamięci z powodu i po pewnym googlingu prawdopodobnie po prostu wzrośnie -XX:MaxPermSizezamiast naprawiać błąd.
Alex Vasseur przeniósł swojego bloga. Oto aktualny link do artykułu o wycieku pamięci.
Kenster
12
Wydaje się, że wątek, do którego prowadzi Julien, został tutaj przeniesiony i warto go przeczytać…
Donal Fellows,
28
Jest to okropne poparcie dla odpowiedzi, która, choć pouczająca, w rzeczywistości w żaden sposób nie odpowiada na pytanie.
Robin
8
Teraz, gdy PermGen został zabity przez Javę 8, czy to w jakikolwiek sposób zmienia tę odpowiedź?
Zła pralka
13
@Robin: Nie zgadzam się. Pytanie dotyczy tego, jak prawidłowo używać ThreadLocal, aby w pełni zrozumieć dowolną koncepcję (tutaj ThreadLocal), ważne jest również, aby zrozumieć, jak jej nie używać i ryzykować niestosownym użyciem, i na tym właśnie polega odpowiedź Phila. Cieszę się, że opisał tę kwestię, która nie została ujęta w żadnej innej odpowiedzi. Wszystkie te głosy są zasłużone. Uważam, że SO powinno budować zrozumienie pojęć, a nie tylko być stroną zapewniania jakości.
Saurabh Patil
166
Wiele frameworków używa ThreadLocals do utrzymywania kontekstu związanego z bieżącym wątkiem. Na przykład, gdy bieżąca transakcja jest przechowywana w ThreadLocal, nie musisz przekazywać jej jako parametru przy każdym wywołaniu metody, na wypadek, gdyby ktoś na stosie potrzebował do niej dostępu. Aplikacje internetowe mogą przechowywać informacje o bieżącym żądaniu i sesji w ThreadLocal, aby aplikacja miała do nich łatwy dostęp. Dzięki Guice możesz używać ThreadLocals podczas implementowania niestandardowych zakresów dla wstrzykiwanych obiektów (domyślnie zakresy serwletów Guice najprawdopodobniej również z nich korzystają).
ThreadLocals to jeden z rodzajów zmiennych globalnych (choć nieco mniej złych, ponieważ są ograniczone do jednego wątku), dlatego należy zachować ostrożność podczas ich używania, aby uniknąć niepożądanych efektów ubocznych i wycieków pamięci. Zaprojektuj swoje interfejsy API, aby wartości ThreadLocal były zawsze automatycznie usuwane, gdy nie są już potrzebne, i że nieprawidłowe użycie interfejsu API nie będzie możliwe (na przykład w ten sposób ). ThreadLocals może być użyty do czyszczenia kodu, a w niektórych rzadkich przypadkach są one jedynym sposobem, aby coś zadziałało (mój obecny projekt miał dwa takie przypadki; są one udokumentowane tutaj w „Static Fields and Global Variables”).
Właśnie w ten sposób środowisko exPOJO (www.expojo.com) umożliwia dostęp do ORM Session / PersistenceManager bez potrzeby narzucania adnotacji i zastrzyków. Jest to coś w rodzaju „wstrzyknięcia nici” zamiast „wstrzyknięcia obiektu”. Zapewnia dostęp do zależności bez konieczności (i narzutu) osadzania ich w każdym obiekcie, który może potrzebować tych zależności. To niesamowite, jak „lekka” może być struktura szkieletowa DI, gdy stosuje się wstrzyknięcie gwintu zamiast klasycznej DI (np. Wiosna itp.)
Volksman
27
Dlaczego musiałem przewijać do tej pory, aby znaleźć tę istotną odpowiedź !?
Jeremy Stein
1
@Esko, W twoim projekcie użycie ThreadLocal w hashcode i equals jest niepotrzebne. Lepszym rozwiązaniem jest utworzenie lub przekazanie odwołania do funkcji hashcode / equals, ilekroć jest to potrzebne. W ten sposób możesz całkowicie uniknąć konieczności hackowania ThreadLocal.
W Javie, jeśli masz układ odniesienia, który może różnić się w zależności od wątku, możesz przekazać ten układ do każdej metody, która go potrzebuje (lub może potrzebować), lub powiązać układ z wątkiem. Przekazywanie punktu odniesienia wszędzie może być wykonalne, jeśli wszystkie twoje metody muszą już przekazywać wspólną zmienną „kontekstową”.
Jeśli tak nie jest, możesz nie chcieć zaśmiecać podpisów metod dodatkowym parametrem. W świecie bez wątków można rozwiązać problem z odpowiednikiem zmiennej globalnej w języku Java. W słowie wątkowym odpowiednikiem zmiennej globalnej jest zmienna lokalna.
Dlatego powinieneś unikać miejscowych wątków w taki sam sposób, jak unika się globalizacji. Nie mogę zaakceptować, że tworzenie globałów (miejscowych wątków) jest OK, zamiast przekazywać wartości, ludzie nie lubią, ponieważ często ujawniają problemy architektury, których nie chcą naprawiać.
Juan Mendes,
10
Być może ... Może to być przydatne, jeśli masz ogromną, istniejącą bazę kodów, do której musisz dodać nową bazę danych, która musi być przekazywana wszędzie , np. Kontekst sesji, transakcja db, zalogowany użytkownik itp. .
Cornel Masson
6
Wyrazy uznania za korzystanie z danych zamiast danych.
głęboki
To mnie zaciekawiło. 'datum' is the singular form and 'data' is the plural form.
Siddhartha
23
Jest bardzo dobry przykład w książce Java Concurrency in Practice . Tam, gdzie autor ( Joshua Bloch ) wyjaśnia, w jaki sposób ograniczanie wątków jest jednym z najprostszych sposobów na osiągnięcie bezpieczeństwa wątków, a ThreadLocal jest bardziej formalnym sposobem utrzymywania ograniczania wątków. Na koniec wyjaśnia również, w jaki sposób ludzie mogą go nadużywać, używając go jako zmiennych globalnych.
Skopiowałem tekst ze wspomnianej książki, ale brakuje kodu 3.10, ponieważ nie jest ważne, aby zrozumieć, gdzie powinien być używany ThreadLocal.
Zmienne lokalne wątków są często używane, aby zapobiec współużytkowaniu w projektach opartych na zmiennych Singletonach lub zmiennych globalnych. Na przykład aplikacja jednowątkowa może utrzymywać globalne połączenie z bazą danych inicjowane podczas uruchamiania, aby uniknąć konieczności przekazywania połączenia do każdej metody. Ponieważ połączenia JDBC mogą nie być bezpieczne dla wątków, aplikacja wielowątkowa korzystająca z połączenia globalnego bez dodatkowej koordynacji również nie jest bezpieczna dla wątków. Używając ThreadLocal do przechowywania połączenia JDBC, tak jak w ConnectionHolder na Listingu 3.10, każdy wątek będzie miał własne połączenie.
ThreadLocal jest szeroko stosowany we wdrażaniu struktur aplikacji. Na przykład kontenery J2EE kojarzą kontekst transakcji z wątkiem wykonawczym na czas trwania wywołania EJB. Można to łatwo zaimplementować za pomocą statycznego Lokalnego wątku przechowującego kontekst transakcji: gdy kod frameworka musi określić, która transakcja jest aktualnie wykonywana, pobiera kontekst transakcji z tego obiektu ThreadLocal. Jest to wygodne, ponieważ zmniejsza potrzebę przekazywania informacji kontekstu wykonania do każdej metody, ale łączy dowolny kod korzystający z tego mechanizmu ze strukturą.
Łatwo jest nadużywać ThreadLocal, traktując jego właściwość ograniczania wątków jako licencję na używanie zmiennych globalnych lub jako środek do tworzenia „ukrytych” argumentów metod. Podobnie jak zmienne globalne, zmienne lokalne wątków mogą niekorzystnie wpływać na możliwość ponownego użycia i wprowadzać ukryte sprzężenia między klasami, dlatego też należy ich używać ostrożnie.
Zasadniczo, gdy potrzebujesz wartości zmiennej zależnej od bieżącego wątku i nie jest wygodne dołączanie wartości do wątku w inny sposób (na przykład wątek podklasy).
Typowy przypadek ma miejsce, gdy jakiś inny framework utworzył wątek, w którym działa twój kod, np. Kontener serwletu, lub gdy bardziej sensowne jest użycie ThreadLocal, ponieważ twoja zmienna jest wtedy „w swoim logicznym miejscu” (zamiast zmiennej zwisające z podklasy Wątek lub na innej mapie mieszającej).
Niektóre osoby opowiadają się za użyciem ThreadLocal jako sposobu na dołączenie „ID wątku” do każdego wątku w pewnych współbieżnych algorytmach, w których potrzebny jest numer wątku (patrz np. Herlihy i Shavit). W takich przypadkach sprawdź, czy naprawdę dostajesz zasiłek!
Czy można użyć ThreadLocal do przechowywania śladu wykonania
gaurav
13
ThreadLocal w Javie został wprowadzony w JDK 1.2, ale później został wygenerowany w JDK 1.5 w celu wprowadzenia bezpieczeństwa typu w zmiennej ThreadLocal.
ThreadLocal może być powiązany z zakresem wątku, cały kod wykonywany przez Thread ma dostęp do zmiennych ThreadLocal, ale dwa wątki nie widzą się nawzajem Zmienna ThreadLocal.
Każdy wątek zawiera wyłączną kopię zmiennej ThreadLocal, która kwalifikuje się do wyrzucania elementów bezużytecznych po zakończeniu lub śmierci wątku, normalnie lub z powodu wyjątku, biorąc pod uwagę, że zmienna ThreadLocal nie ma żadnych innych aktywnych odniesień.
Zmienne ThreadLocal w Javie są zwykle prywatnymi polami statycznymi w klasach i utrzymują swój stan w wątku.
Czy można użyć ThreadLocal do przechowywania śladu wykonania
gaurav
11
Dokumentacja mówi bardzo dobrze: „każdy wątek, który uzyskuje dostęp do [zmiennej lokalnej wątku] (za pomocą metody get lub set) ma swoją własną, niezależnie inicjowaną kopię zmiennej”.
Używasz jednego, gdy każdy wątek musi mieć własną kopię czegoś. Domyślnie dane są dzielone między wątkami.
Domyślnie obiekty statyczne lub obiekty jawnie przekazywane między wątkami, w których oba wątki zawierają odwołanie do tego samego obiektu, są współużytkowane. Obiekty zadeklarowane lokalnie w wątku nie są udostępniane (są lokalne dla stosu wątku). Chciałem tylko to wyjaśnić.
Ian Varley
@IanVarley: Dzięki za wskazanie tego. Więc jeśli mamy zmienną zadeklarowaną i używaną w klasie MyThread, to czy potrzebujemy ThreadLocal w tym przypadku? Przykład: klasa MyThread rozszerza Wątek {Ciąg var1 ;, int var2; } w tym przypadku var1 i var2 będą częścią stosu wątków i nie będą udostępniane, więc czy w tym przypadku potrzebujemy ThreadLocal? Mogę całkowicie się mylić, proszę, prowadź.
Jayesh
4
Jeśli każdy wątek chce mieć własną kopię czegoś, dlaczego nie można go po prostu zadeklarować jako lokalny (co zawsze jest bezpieczne dla wątku)?
sudeepdino008
@ sudeepdino008 nie zawsze masz kontrolę nad tworzeniem tych wątków (frameworki, biblioteki wątków)
JMess 24.04.18
10
Serwer Webapp może przechowywać pulę wątków, a ThreadLocalvar powinien zostać usunięty przed odpowiedzią na klienta, dlatego bieżący wątek może zostać ponownie wykorzystany przy następnym żądaniu.
Dwa przypadki użycia, w których można zastosować zmienną Threadlocal -
1- Gdy mamy wymaganie skojarzenia stanu z wątkiem (np. Identyfikator użytkownika lub identyfikator transakcji). Zdarza się to zwykle w przypadku aplikacji internetowej, z którą każde żądanie kierowane do serwletu ma przypisany unikalny identyfikator transakcji.
// This class will provide a thread local variable which// will provide a unique ID for each threadclassThreadId{// Atomic integer containing the next thread ID to be assignedprivatestaticfinalAtomicInteger nextId =newAtomicInteger(0);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.<Integer>withInitial(()->{return nextId.getAndIncrement();});// Returns the current thread's unique ID, assigning it if necessarypublicstaticint get(){return threadId.get();}}
Zauważ, że tutaj metoda withInitial jest implementowana przy użyciu wyrażenia lambda.
2- Innym przypadkiem użycia jest sytuacja, gdy chcemy mieć instancję bezpieczną dla wątków i nie chcemy używać synchronizacji, ponieważ koszt wydajności z synchronizacją jest większy. Jednym z takich przypadków jest użycie SimpleDateFormat. Ponieważ SimpleDateFormat nie jest bezpieczny dla wątków, musimy zapewnić mechanizm, aby był bezpieczny dla wątków.
publicclassThreadLocalDemo1implementsRunnable{// threadlocal variable is createdprivatestaticfinalThreadLocal<SimpleDateFormat> dateFormat =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){System.out.println("Initializing SimpleDateFormat for - "+Thread.currentThread().getName());returnnewSimpleDateFormat("dd/MM/yyyy");}};publicstaticvoid main(String[] args){ThreadLocalDemo1 td =newThreadLocalDemo1();// Two threads are createdThread t1 =newThread(td,"Thread-1");Thread t2 =newThread(td,"Thread-2");
t1.start();
t2.start();}@Overridepublicvoid 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(newDate()));}}
Nadal nie widziałem korzyści z używania ThreadLocal do wygenerowania unikalnego identyfikatora w przykładzie 1, jeśli Twoim jedynym celem jest zwrócenie przyrostowej unikalnej wartości całkowitej. `` public class ThreadId {// Atomowa liczba całkowita zawierająca następny identyfikator wątku, który ma zostać przypisany prywatnej statycznej końcowej AtomicInteger nextId = new AtomicInteger (0); // Zwraca unikalny identyfikator bieżącego wątku, przypisując go w razie potrzeby public static int get () {return nextId..getAndIncrement (); }} ``
dashenswen
7
Od wydania Java 8 istnieje bardziej deklaratywny sposób inicjowania ThreadLocal:
ThreadLocal<Cipher> local =ThreadLocal.withInitial(()->"init value");
Do czasu wydania Java 8 trzeba było wykonać następujące czynności:
ThreadLocal<String> local =newThreadLocal<String>(){@OverrideprotectedString initialValue(){return"init value";}};
Ponadto, jeśli metoda instancji (konstruktor, metoda fabryczna) klasy, dla której jest używana ThreadLocal, nie przyjmuje żadnych parametrów, możesz po prostu użyć odwołań do metod (wprowadzonych w Javie 8):
classNotThreadSafe{// no parameterspublicNotThreadSafe(){}}ThreadLocal<NotThreadSafe> container =ThreadLocal.withInitial(NotThreadSafe::new);
Uwaga:
Ocena jest leniwa, ponieważ przekazujesz java.util.function.Supplierlambda, która jest oceniana tylko wtedy, gdy ThreadLocal#getjest wywoływana, ale wartość nie była wcześniej oceniana.
Musisz być bardzo ostrożny ze wzorem ThreadLocal. Istnieje kilka poważnych wad, takich jak wspomniany Phil, ale jednym z nich nie było to, aby upewnić się, że kod, który konfiguruje kontekst ThreadLocal, nie jest „ponownie dostępny”.
Złe rzeczy mogą się zdarzyć, gdy kod ustawiający informacje zostanie uruchomiony drugi lub trzeci raz, ponieważ informacje w twoim wątku mogą zacząć mutować, gdy się tego nie spodziewałeś. Dlatego upewnij się, że informacje ThreadLocal nie zostały ustawione przed ponownym ustawieniem.
Ponowne wejście nie stanowi problemu, jeśli kod jest przygotowany na to. Przy wejściu zanotuj, czy zmienna jest już ustawiona, a przy wyjściu przywróć jej poprzednią wartość (jeśli była) lub usuń ją (jeśli nie).
supercat
1
@Jeff, stary, dotyczy to każdego kodu, który piszesz, nie tylko ThreadLocalwzorca. Jeśli to zrobisz, F(){ member=random(); F2(); write(member); }a F2 zastąpi członka nową wartością, wtedy oczywiście write(member)nie zapisze już edytowanej liczby random(). To dosłownie zdrowy rozsądek. Podobnie, jeśli tak F(){ F(); }, to gd powodzenia z nieskończoną pętlą! Jest to prawdą wszędzie i nie jest specyficzne ThreadLocal.
Pacerier
@Jeff, przykład kodu?
Pacerier
5
kiedy?
Jeśli obiekt nie jest bezpieczny dla wątków, zamiast synchronizacji, która utrudnia skalowalność, należy dać jeden obiekt każdemu wątkowi i zachować zakres wątku, którym jest ThreadLocal. Jednym z najczęściej używanych, ale niechronionych wątków obiektów, jest połączenie z bazą danych i JMSConnection.
W jaki sposób ?
Jednym z przykładów jest platforma Spring, która mocno wykorzystuje ThreadLocal do zarządzania transakcjami za kulisami, utrzymując te obiekty połączeń w zmiennych ThreadLocal. Na wysokim poziomie, po rozpoczęciu transakcji, otrzymuje połączenie (i wyłącza automatyczne zatwierdzanie) i utrzymuje je w ThreadLocal. przy kolejnych wywołaniach db używa tego samego połączenia do komunikacji z db. Na koniec pobiera połączenie z ThreadLocal i zatwierdza (lub przywraca) transakcję i zwalnia połączenie.
Myślę, że log4j używa także ThreadLocal do obsługi MDC.
ThreadLocal jest przydatny, gdy chcesz mieć stan, który nie powinien być współużytkowany przez różne wątki, ale powinien być dostępny z każdego wątku przez cały okres jego użytkowania.
Jako przykład wyobraź sobie aplikację internetową, w której każde żądanie jest obsługiwane przez inny wątek. Wyobraź sobie, że do każdego żądania potrzebujesz fragmentu danych wiele razy, co jest dość drogie do obliczenia. Jednak te dane mogły ulec zmianie dla każdego przychodzącego żądania, co oznacza, że nie można użyć zwykłej pamięci podręcznej. Prostym i szybkim rozwiązaniem tego problemu byłoby posiadanie ThreadLocalzmiennej przechowującej dostęp do tych danych, dzięki czemu trzeba ją obliczyć tylko raz dla każdego żądania. Oczywiście ten problem można również rozwiązać bez użycia ThreadLocal, ale opracowałem go w celach ilustracyjnych.
To powiedziawszy, pamiętajcie, że ThreadLocalsą one zasadniczo formą państwa globalnego. W rezultacie ma wiele innych implikacji i należy go stosować dopiero po rozważeniu wszystkich innych możliwych rozwiązań.
ThreadLocals NIE są stanem globalnym, chyba że zostanie ustawiony na stan globalny. Są to praktycznie zawsze dostępne zasoby stosu. Możesz symulować wątek lokalnie, po prostu przekazując tę zmienną we wszystkich swoich metodach (co jest opóźnione); to nie czyni go globalnym stanem ...
Enerccio,
Jest to forma stanu globalnego w tym sensie, że jest dostępna z dowolnego miejsca w kodzie (w kontekście tego samego wątku). Jest to związane ze wszystkimi konsekwencjami, takimi jak brak możliwości uzasadnienia, kto czyta i zapisuje tę wartość. Używanie parametru funkcji nie jest opóźnione, jest to dobra praktyka promująca czyste interfejsy. Zgadzam się jednak z tobą, że przekazanie parametru na całej głębokości bazy kodu jest zapachem kodu. Ale wierzę również, że w wielu przypadkach użycie ThreadLocal jest przede wszystkim zapachem kodu, który cię tu zaprowadził, więc należy to ponownie rozważyć.
Dimos,
Może być po prostu częścią stanu obiektu, nie musi być używany globalnie. Jasne, masz trochę narzutów, ale jeśli wiele obiektów musi mieć inny stan na wątek, możesz użyćThreadLocal jako pola obiektu ...
Enerccio,
Masz rację. Prawdopodobnie natknąłem się na kilka niewłaściwych zastosowań ThreadLocal, gdzie był on dostępny na całym świecie. Jak powiedziałeś, nadal może być używany jako pole klasy ograniczające widoczność.
Dimos,
4
Nie ma tu nic nowego, ale dzisiaj odkryłem, że ThreadLocaljest to bardzo przydatne, gdy korzystam z Bean Validation w aplikacji internetowej. Komunikaty sprawdzania poprawności są zlokalizowane, ale domyślnie są używane Locale.getDefault(). Możesz skonfigurować Validatorinny MessageInterpolator, ale nie ma sposobu, aby określić, Localekiedy zadzwoniszvalidate . Możesz więc stworzyć statyczny ThreadLocal<Locale>(lub jeszcze lepiej, ogólny kontener z innymi rzeczami, które mogą być potrzebne, ThreadLocala następnie MessageInterpolatorwybrać niestandardowy Localez tego. Kolejnym krokiem jest napisanie, ServletFilterktóry używa wartości sesji lub request.getLocale()wybór ustawień regionalnych i przechowywania to w twojej ThreadLocalreferencji.
Jak wspomniano w @unknown (google), jego użycie polega na zdefiniowaniu zmiennej globalnej, w której wartość odniesienia może być unikalna w każdym wątku. Jego użycie zwykle oznacza przechowywanie pewnego rodzaju informacji kontekstowych, które są powiązane z bieżącym wątkiem wykonania.
Używamy go w środowisku Java EE, aby przekazać tożsamość użytkownika do klas, które nie są świadome Java EE (nie mają dostępu do HttpSession lub EJB SessionContext). W ten sposób kod, który wykorzystuje tożsamość do operacji opartych na bezpieczeństwie, może uzyskać dostęp do tożsamości z dowolnego miejsca, bez konieczności jawnego przekazywania jej przy każdym wywołaniu metody.
Cykl operacji żądanie / odpowiedź w większości wywołań Java EE ułatwia ten rodzaj użytkowania, ponieważ zapewnia dobrze zdefiniowane punkty wejścia i wyjścia do ustawiania i wyłączania ThreadLocal.
ThreadLocal zapewni dostęp do obiektu zmiennego przez wiele wątków w niezsynchronizowanej metodzie jest zsynchronizowany, co oznacza, że obiekt zmienny będzie niezmienny w obrębie metody.
Osiąga się to poprzez podanie nowej instancji obiektu zmiennego dla każdej próby uzyskania dostępu do wątku. Jest to lokalna kopia dla każdego wątku. Jest to pewien hack przy tworzeniu zmiennej instancji w metodzie dostępnej jak zmienna lokalna. Ponieważ wiesz, że zmienna lokalna metody jest dostępna tylko dla wątku, jedną różnicą jest; zmienne lokalne metody nie będą dostępne dla wątku po zakończeniu wykonywania metody, w której obiekt zmienny współdzielony z Threadlocal będzie dostępny dla wielu metod, dopóki go nie wyczyścimy.
Zgodnie z definicją:
Klasa ThreadLocal w Javie umożliwia tworzenie zmiennych, które mogą być odczytywane i zapisywane tylko przez ten sam wątek. Dlatego nawet jeśli dwa wątki wykonują ten sam kod, a kod zawiera odwołanie do zmiennej ThreadLocal, wówczas oba wątki nie widzą nawzajem swoich zmiennych ThreadLocal.
Każda Threadw java zawiera ThreadLocalMapw sobie.
Gdzie
Key=OneThreadLocal object shared across threads.
value =Mutable object which has to be used synchronously,this will be instantiated for each thread.
Osiągnięcie ThreadLocal:
Teraz stwórz klasę opakowania dla ThreadLocal, która będzie przechowywać zmienny obiekt jak poniżej (z lub bez initialValue()). Teraz getter i setter tego opakowania będą działały na instancji Threadlocal zamiast obiektu mutable.
Jeśli getter () z Threadlocal nie znalazł żadnej wartości z w Threadlocalmap na Thread; następnie wywoła funkcję initialValue (), aby uzyskać swoją prywatną kopię dotyczącą wątku.
classSimpleDateFormatInstancePerThread{privatestaticfinalThreadLocal<SimpleDateFormat> dateFormatHolder =newThreadLocal<SimpleDateFormat>(){@OverrideprotectedSimpleDateFormat initialValue(){SimpleDateFormat dateFormat =newSimpleDateFormat("yyyy-MM-dd"){
UUID id = UUID.randomUUID();@OverridepublicString 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
*/publicstaticDateFormat getDateFormatter(){return dateFormatHolder.get();}publicstaticvoid cleanup(){
dateFormatHolder.remove();}}
Teraz wrapper.getDateFormatter()zadzwoni threadlocal.get()i sprawdzi, czy currentThread.threadLocalMapzawiera to wystąpienie (Threadlocal).
Jeśli tak, zwróć wartość (SimpleDateFormat) dla odpowiedniej
instancji Threadlocal, w przeciwnym razie dodaj mapę z tą instancją ThreadLocal, initialValue ().
W ten sposób osiągnięto bezpieczeństwo wątków w tej klasie zmiennych; przez każdy wątek działa z własną instancją mutable, ale z tą samą instancją ThreadLocal. Oznacza, że cały wątek będzie współdzielił tę samą instancję ThreadLocal jako klucz, ale inną instancję SimpleDateFormat jako wartość.
Wywołanie getConnection spowoduje zwrócenie połączenia powiązanego z tym wątkiem. To samo można zrobić z innymi właściwościami, takimi jak dateformat, kontekst transakcji, którego nie chcesz udostępniać między wątkami.
Mogłeś również użyć zmiennych lokalnych do tego samego, ale te zasoby zwykle zajmują dużo czasu podczas tworzenia, więc nie chcesz ich tworzyć ponownie za każdym razem, gdy wykonujesz z nimi logikę biznesową. Jednak wartości ThreadLocal są przechowywane w samym obiekcie wątku i gdy tylko wątek zostanie wyrzucony, elementy te również znikają.
Ten link bardzo dobrze wyjaśnia użycie ThreadLocal.
W tym przykładzie jest poważny problem: kto jest odpowiedzialny za zamknięcie połączenia? Zamiast tworzyć to połączenie jako wartość initiali, pozwól konsumentowi połączenia jawnie utworzyć połączenie i powiązać je z wątkiem za pośrednictwem ThreadLocal. Twórca połączenia jest wówczas również odpowiedzialny za zamknięcie. W tym przykładzie nie jest to jasne. Tworzenie i wiązanie połączenia można również ukryć w prostym frameworku za pomocą Transaction.begin () i Transaction.end ().
Rick-Rainer Ludwig
2
ThreadLocal klasa w Java umożliwia tworzenie zmiennych, które mogą być odczytywane i zapisywane przez tego samego wątku tylko. Dlatego nawet jeśli dwa wątki wykonują ten sam kod, a kod zawiera odwołanie do zmiennej ThreadLocal, wówczas oba wątki nie widzą nawzajem swoich zmiennych ThreadLocal.
Istnieją 3 scenariusze korzystania z pomocnika klasy, takiego jak SimpleDateFormat w kodzie wielowątkowym, z których najlepszym jest użycie ThreadLocal
Scenariusze
1- Korzystanie z podobnego obiektu udostępniania za pomocą mechanizmu blokady lub synchronizacji , który spowalnia działanie aplikacji
2- Używanie jako obiektu lokalnego w metodzie
W tym scenariuszu, jeśli mamy 4 wątki, każdy wywołuje metodę 1000 razy, to mamy 4000 obiektów SimpleDateFormat utworzonych i oczekujących na ich usunięcie przez GC
3- Korzystanie z ThreadLocal
jeśli mamy 4 wątki i podaliśmy każdemu wątkowi jedną instancję SimpleDateFormat,
więc mamy 4 wątki , 4 obiekty SimpleDateFormat.
Nie ma potrzeby blokowania mechanizmu oraz tworzenia i niszczenia obiektów. (Dobra złożoność czasu i złożoność przestrzeni)
[Dla odniesienia] ThreadLocal nie może rozwiązać problemów z aktualizacją współdzielonego obiektu. Zalecane jest użycie obiektu staticThreadLocal, który jest wspólny dla wszystkich operacji w tym samym wątku. [Obowiązkowe] metoda remove () musi być zaimplementowana przez zmienne ThreadLocal, szczególnie przy użyciu pul wątków, w których wątki są często ponownie wykorzystywane. W przeciwnym razie może to wpłynąć na późniejszą logikę biznesową i spowodować nieoczekiwane problemy, takie jak wyciek pamięci.
Buforowanie, czasami musisz obliczyć tę samą wartość dużo czasu, więc przechowując ostatni zestaw danych wejściowych w metodzie, a wynik możesz przyspieszyć kod. Używając Thread Local Storage, nie musisz myśleć o blokowaniu.
ThreadLocal jest specjalnie wyposażoną funkcją JVM, która zapewnia izolowane miejsce do przechowywania tylko dla wątków. podobnie jak wartość zmiennej o zasięgu instancji są powiązane tylko z danym wystąpieniem klasy. każdy obiekt ma swoje jedyne wartości i nie mogą widzieć siebie nawzajem. podobnie jest koncepcja zmiennych ThreadLocal, są one lokalne dla wątku w sensie instancji obiektu inny wątek, z wyjątkiem tego, który go utworzył, nie może go zobaczyć. Spójrz tutaj
import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.IntStream;publicclassThreadId{privatestaticfinalAtomicInteger nextId =newAtomicInteger(1000);// Thread local variable containing each thread's IDprivatestaticfinalThreadLocal<Integer> threadId =ThreadLocal.withInitial(()-> nextId.getAndIncrement());// Returns the current thread's unique ID, assigning it if necessarypublicstaticint get(){return threadId.get();}publicstaticvoid main(String[] args){newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();newThread(()->IntStream.range(1,3).forEach(i ->{System.out.println(Thread.currentThread().getName()+" >> "+newThreadId().get());})).start();}}
Threadlocal zapewnia bardzo łatwy sposób na ponowne użycie obiektów przy zerowym koszcie.
Miałem sytuację, w której wiele wątków tworzyło obraz modyfikowalnej pamięci podręcznej przy każdym powiadomieniu o aktualizacji.
Użyłem Threadlocal dla każdego wątku, a następnie każdy wątek musiałby po prostu zresetować stary obraz, a następnie zaktualizować go ponownie z pamięci podręcznej przy każdym powiadomieniu o aktualizacji.
Zwykłe obiekty wielokrotnego użytku z pul obiektów są powiązane z kosztami bezpieczeństwa wątków, podczas gdy takie podejście nie ma żadnego.
Wypróbuj ten mały przykład, aby poznać zmienną ThreadLocal:
publicclassBookimplementsRunnable{privatestaticfinalThreadLocal<List<String>> WORDS =ThreadLocal.withInitial(ArrayList::new);privatefinalString bookName;// It is also the thread's nameprivatefinalList<String> words;publicBook(String bookName,List<String> words){this.bookName = bookName;this.words =Collections.unmodifiableList(words);}publicvoid run(){
WORDS.get().addAll(words);System.out.printf("Result %s: '%s'.%n", bookName,String.join(", ", WORDS.get()));}publicstaticvoid main(String[] args){Thread t1 =newThread(newBook("BookA",Arrays.asList("wordA1","wordA2","wordA3")));Thread t2 =newThread(newBook("BookB",Arrays.asList("wordB1","wordB2")));
t1.start();
t2.start();}}
Dane wyjściowe konsoli, jeśli wątek BookA zostanie wykonany jako pierwszy:
Wynik BookA: „słowo A1, słowo A2, słowo A3”.
Wynik BookB: „słowo B1, słowo B2”.
Dane wyjściowe konsoli, jeśli wątek BookB zostanie zrobiony jako pierwszy:
Wynik BookB: 'wordB1, wordB2'.
Księga wyników A: „słowo A1, słowo A2, słowo A3”.
RequestUtils.getCurrentRequestThreadLocal()
. Nie oznacza to jednak, że jest to bardzo eleganckie, ale wynika to z faktu, że sam ThreadLocal w większości przypadków nie jest zbyt elegancki.Odpowiedzi:
Jednym z możliwych (i powszechnych) zastosowań jest posiadanie obiektu, który nie jest bezpieczny dla wątków, ale chcesz uniknąć synchronizowania dostępu do tego obiektu (patrzę na ciebie, SimpleDateFormat ). Zamiast tego nadaj każdemu wątkowi własną instancję obiektu.
Na przykład:
Dokumentacja .
źródło
SimpleDateFormat
. Być może lepiej jest użyć bezpiecznej dla wątków alternatywy . Jeśli zgadzasz się, że singletons są złe potemThreadLocal
jest jeszcze gorzej.Ponieważ a
ThreadLocal
jest odniesieniem do danych w danym obszarzeThread
, możesz skończyć przeciekami klasy podczas używaniaThreadLocal
s na serwerach aplikacji za pomocą pul wątków. Trzeba być bardzo ostrożnym czyszczenia żadnychThreadLocal
jesteś tyget()
lubset()
za pomocąThreadLocal
„sremove()
metody.Jeśli nie wyczyścisz po zakończeniu, wszelkie odwołania do klas załadowanych jako część wdrożonej aplikacji internetowej pozostaną na stałym stosie i nigdy nie zostaną usunięte. Ponowne wdrożenie / cofnięcie aplikacji internetowej nie wyczyści
Thread
odniesienia do klas aplikacji, ponieważThread
nie jest to własność aplikacji internetowej. Każde kolejne wdrożenie spowoduje utworzenie nowej instancji klasy, która nigdy nie będzie zbierana w pamięci.java.lang.OutOfMemoryError: PermGen space
Skończysz z brakami pamięci z powodu i po pewnym googlingu prawdopodobnie po prostu wzrośnie-XX:MaxPermSize
zamiast naprawiać błąd.Jeśli napotkasz te problemy, możesz ustalić, który wątek i klasa zachowuje te referencje, korzystając z narzędzia Eclipse's Memory Analyzer i / lub postępując zgodnie z instrukcjami Franka Kieviet i działaniami następczymi .
Aktualizacja: Ponownie odkryłem wpis na blogu Alexa Vasseura, który pomógł mi wyśledzić niektóre
ThreadLocal
problemy, które miałem.źródło
Wiele frameworków używa ThreadLocals do utrzymywania kontekstu związanego z bieżącym wątkiem. Na przykład, gdy bieżąca transakcja jest przechowywana w ThreadLocal, nie musisz przekazywać jej jako parametru przy każdym wywołaniu metody, na wypadek, gdyby ktoś na stosie potrzebował do niej dostępu. Aplikacje internetowe mogą przechowywać informacje o bieżącym żądaniu i sesji w ThreadLocal, aby aplikacja miała do nich łatwy dostęp. Dzięki Guice możesz używać ThreadLocals podczas implementowania niestandardowych zakresów dla wstrzykiwanych obiektów (domyślnie zakresy serwletów Guice najprawdopodobniej również z nich korzystają).
ThreadLocals to jeden z rodzajów zmiennych globalnych (choć nieco mniej złych, ponieważ są ograniczone do jednego wątku), dlatego należy zachować ostrożność podczas ich używania, aby uniknąć niepożądanych efektów ubocznych i wycieków pamięci. Zaprojektuj swoje interfejsy API, aby wartości ThreadLocal były zawsze automatycznie usuwane, gdy nie są już potrzebne, i że nieprawidłowe użycie interfejsu API nie będzie możliwe (na przykład w ten sposób ). ThreadLocals może być użyty do czyszczenia kodu, a w niektórych rzadkich przypadkach są one jedynym sposobem, aby coś zadziałało (mój obecny projekt miał dwa takie przypadki; są one udokumentowane tutaj w „Static Fields and Global Variables”).
źródło
W Javie, jeśli masz układ odniesienia, który może różnić się w zależności od wątku, możesz przekazać ten układ do każdej metody, która go potrzebuje (lub może potrzebować), lub powiązać układ z wątkiem. Przekazywanie punktu odniesienia wszędzie może być wykonalne, jeśli wszystkie twoje metody muszą już przekazywać wspólną zmienną „kontekstową”.
Jeśli tak nie jest, możesz nie chcieć zaśmiecać podpisów metod dodatkowym parametrem. W świecie bez wątków można rozwiązać problem z odpowiednikiem zmiennej globalnej w języku Java. W słowie wątkowym odpowiednikiem zmiennej globalnej jest zmienna lokalna.
źródło
'datum' is the singular form and 'data' is the plural form.
Jest bardzo dobry przykład w książce Java Concurrency in Practice . Tam, gdzie autor ( Joshua Bloch ) wyjaśnia, w jaki sposób ograniczanie wątków jest jednym z najprostszych sposobów na osiągnięcie bezpieczeństwa wątków, a ThreadLocal jest bardziej formalnym sposobem utrzymywania ograniczania wątków. Na koniec wyjaśnia również, w jaki sposób ludzie mogą go nadużywać, używając go jako zmiennych globalnych.
Skopiowałem tekst ze wspomnianej książki, ale brakuje kodu 3.10, ponieważ nie jest ważne, aby zrozumieć, gdzie powinien być używany ThreadLocal.
źródło
Zasadniczo, gdy potrzebujesz wartości zmiennej zależnej od bieżącego wątku i nie jest wygodne dołączanie wartości do wątku w inny sposób (na przykład wątek podklasy).
Typowy przypadek ma miejsce, gdy jakiś inny framework utworzył wątek, w którym działa twój kod, np. Kontener serwletu, lub gdy bardziej sensowne jest użycie ThreadLocal, ponieważ twoja zmienna jest wtedy „w swoim logicznym miejscu” (zamiast zmiennej zwisające z podklasy Wątek lub na innej mapie mieszającej).
Na mojej stronie internetowej omówiłem kilka przykładów i przykładów użycia ThreadLocal, które mogą być również interesujące.
Niektóre osoby opowiadają się za użyciem ThreadLocal jako sposobu na dołączenie „ID wątku” do każdego wątku w pewnych współbieżnych algorytmach, w których potrzebny jest numer wątku (patrz np. Herlihy i Shavit). W takich przypadkach sprawdź, czy naprawdę dostajesz zasiłek!
źródło
ThreadLocal w Javie został wprowadzony w JDK 1.2, ale później został wygenerowany w JDK 1.5 w celu wprowadzenia bezpieczeństwa typu w zmiennej ThreadLocal.
ThreadLocal może być powiązany z zakresem wątku, cały kod wykonywany przez Thread ma dostęp do zmiennych ThreadLocal, ale dwa wątki nie widzą się nawzajem Zmienna ThreadLocal.
Każdy wątek zawiera wyłączną kopię zmiennej ThreadLocal, która kwalifikuje się do wyrzucania elementów bezużytecznych po zakończeniu lub śmierci wątku, normalnie lub z powodu wyjątku, biorąc pod uwagę, że zmienna ThreadLocal nie ma żadnych innych aktywnych odniesień.
Zmienne ThreadLocal w Javie są zwykle prywatnymi polami statycznymi w klasach i utrzymują swój stan w wątku.
Czytaj więcej: ThreadLocal w Javie - Przykładowy program i samouczek
źródło
Dokumentacja mówi bardzo dobrze: „każdy wątek, który uzyskuje dostęp do [zmiennej lokalnej wątku] (za pomocą metody get lub set) ma swoją własną, niezależnie inicjowaną kopię zmiennej”.
Używasz jednego, gdy każdy wątek musi mieć własną kopię czegoś. Domyślnie dane są dzielone między wątkami.
źródło
Serwer Webapp może przechowywać pulę wątków, a
ThreadLocal
var powinien zostać usunięty przed odpowiedzią na klienta, dlatego bieżący wątek może zostać ponownie wykorzystany przy następnym żądaniu.źródło
Dwa przypadki użycia, w których można zastosować zmienną Threadlocal -
1- Gdy mamy wymaganie skojarzenia stanu z wątkiem (np. Identyfikator użytkownika lub identyfikator transakcji). Zdarza się to zwykle w przypadku aplikacji internetowej, z którą każde żądanie kierowane do serwletu ma przypisany unikalny identyfikator transakcji.
Zauważ, że tutaj metoda withInitial jest implementowana przy użyciu wyrażenia lambda.
2- Innym przypadkiem użycia jest sytuacja, gdy chcemy mieć instancję bezpieczną dla wątków i nie chcemy używać synchronizacji, ponieważ koszt wydajności z synchronizacją jest większy. Jednym z takich przypadków jest użycie SimpleDateFormat. Ponieważ SimpleDateFormat nie jest bezpieczny dla wątków, musimy zapewnić mechanizm, aby był bezpieczny dla wątków.
źródło
Od wydania Java 8 istnieje bardziej deklaratywny sposób inicjowania
ThreadLocal
:Do czasu wydania Java 8 trzeba było wykonać następujące czynności:
Ponadto, jeśli metoda instancji (konstruktor, metoda fabryczna) klasy, dla której jest używana
ThreadLocal
, nie przyjmuje żadnych parametrów, możesz po prostu użyć odwołań do metod (wprowadzonych w Javie 8):Uwaga: Ocena jest leniwa, ponieważ przekazujesz
java.util.function.Supplier
lambda, która jest oceniana tylko wtedy, gdyThreadLocal#get
jest wywoływana, ale wartość nie była wcześniej oceniana.źródło
Musisz być bardzo ostrożny ze wzorem ThreadLocal. Istnieje kilka poważnych wad, takich jak wspomniany Phil, ale jednym z nich nie było to, aby upewnić się, że kod, który konfiguruje kontekst ThreadLocal, nie jest „ponownie dostępny”.
Złe rzeczy mogą się zdarzyć, gdy kod ustawiający informacje zostanie uruchomiony drugi lub trzeci raz, ponieważ informacje w twoim wątku mogą zacząć mutować, gdy się tego nie spodziewałeś. Dlatego upewnij się, że informacje ThreadLocal nie zostały ustawione przed ponownym ustawieniem.
źródło
ThreadLocal
wzorca. Jeśli to zrobisz,F(){ member=random(); F2(); write(member); }
a F2 zastąpi członka nową wartością, wtedy oczywiściewrite(member)
nie zapisze już edytowanej liczbyrandom()
. To dosłownie zdrowy rozsądek. Podobnie, jeśli takF(){ F(); }
, to gd powodzenia z nieskończoną pętlą! Jest to prawdą wszędzie i nie jest specyficzneThreadLocal
.kiedy?
Jeśli obiekt nie jest bezpieczny dla wątków, zamiast synchronizacji, która utrudnia skalowalność, należy dać jeden obiekt każdemu wątkowi i zachować zakres wątku, którym jest ThreadLocal. Jednym z najczęściej używanych, ale niechronionych wątków obiektów, jest połączenie z bazą danych i JMSConnection.
W jaki sposób ?
Jednym z przykładów jest platforma Spring, która mocno wykorzystuje ThreadLocal do zarządzania transakcjami za kulisami, utrzymując te obiekty połączeń w zmiennych ThreadLocal. Na wysokim poziomie, po rozpoczęciu transakcji, otrzymuje połączenie (i wyłącza automatyczne zatwierdzanie) i utrzymuje je w ThreadLocal. przy kolejnych wywołaniach db używa tego samego połączenia do komunikacji z db. Na koniec pobiera połączenie z ThreadLocal i zatwierdza (lub przywraca) transakcję i zwalnia połączenie.
Myślę, że log4j używa także ThreadLocal do obsługi MDC.
źródło
ThreadLocal
jest przydatny, gdy chcesz mieć stan, który nie powinien być współużytkowany przez różne wątki, ale powinien być dostępny z każdego wątku przez cały okres jego użytkowania.Jako przykład wyobraź sobie aplikację internetową, w której każde żądanie jest obsługiwane przez inny wątek. Wyobraź sobie, że do każdego żądania potrzebujesz fragmentu danych wiele razy, co jest dość drogie do obliczenia. Jednak te dane mogły ulec zmianie dla każdego przychodzącego żądania, co oznacza, że nie można użyć zwykłej pamięci podręcznej. Prostym i szybkim rozwiązaniem tego problemu byłoby posiadanie
ThreadLocal
zmiennej przechowującej dostęp do tych danych, dzięki czemu trzeba ją obliczyć tylko raz dla każdego żądania. Oczywiście ten problem można również rozwiązać bez użyciaThreadLocal
, ale opracowałem go w celach ilustracyjnych.To powiedziawszy, pamiętajcie, że
ThreadLocal
są one zasadniczo formą państwa globalnego. W rezultacie ma wiele innych implikacji i należy go stosować dopiero po rozważeniu wszystkich innych możliwych rozwiązań.źródło
ThreadLocal
jako pola obiektu ...ThreadLocal
, gdzie był on dostępny na całym świecie. Jak powiedziałeś, nadal może być używany jako pole klasy ograniczające widoczność.Nie ma tu nic nowego, ale dzisiaj odkryłem, że
ThreadLocal
jest to bardzo przydatne, gdy korzystam z Bean Validation w aplikacji internetowej. Komunikaty sprawdzania poprawności są zlokalizowane, ale domyślnie są używaneLocale.getDefault()
. Możesz skonfigurowaćValidator
innyMessageInterpolator
, ale nie ma sposobu, aby określić,Locale
kiedy zadzwoniszvalidate
. Możesz więc stworzyć statycznyThreadLocal<Locale>
(lub jeszcze lepiej, ogólny kontener z innymi rzeczami, które mogą być potrzebne,ThreadLocal
a następnieMessageInterpolator
wybrać niestandardowyLocale
z tego. Kolejnym krokiem jest napisanie,ServletFilter
który używa wartości sesji lubrequest.getLocale()
wybór ustawień regionalnych i przechowywania to w twojejThreadLocal
referencji.źródło
Jak wspomniano w @unknown (google), jego użycie polega na zdefiniowaniu zmiennej globalnej, w której wartość odniesienia może być unikalna w każdym wątku. Jego użycie zwykle oznacza przechowywanie pewnego rodzaju informacji kontekstowych, które są powiązane z bieżącym wątkiem wykonania.
Używamy go w środowisku Java EE, aby przekazać tożsamość użytkownika do klas, które nie są świadome Java EE (nie mają dostępu do HttpSession lub EJB SessionContext). W ten sposób kod, który wykorzystuje tożsamość do operacji opartych na bezpieczeństwie, może uzyskać dostęp do tożsamości z dowolnego miejsca, bez konieczności jawnego przekazywania jej przy każdym wywołaniu metody.
Cykl operacji żądanie / odpowiedź w większości wywołań Java EE ułatwia ten rodzaj użytkowania, ponieważ zapewnia dobrze zdefiniowane punkty wejścia i wyjścia do ustawiania i wyłączania ThreadLocal.
źródło
Zgodnie z definicją:
Każda
Thread
w java zawieraThreadLocalMap
w sobie.Gdzie
Osiągnięcie ThreadLocal:
Teraz stwórz klasę opakowania dla ThreadLocal, która będzie przechowywać zmienny obiekt jak poniżej (z lub bez
initialValue()
).Teraz getter i setter tego opakowania będą działały na instancji Threadlocal zamiast obiektu mutable.
Jeśli getter () z Threadlocal nie znalazł żadnej wartości z w Threadlocalmap na
Thread
; następnie wywoła funkcję initialValue (), aby uzyskać swoją prywatną kopię dotyczącą wątku.Teraz
wrapper.getDateFormatter()
zadzwonithreadlocal.get()
i sprawdzi, czycurrentThread.threadLocalMap
zawiera to wystąpienie (Threadlocal).Jeśli tak, zwróć wartość (SimpleDateFormat) dla odpowiedniej
instancji Threadlocal, w przeciwnym razie dodaj mapę z tą instancją ThreadLocal, initialValue ().
W ten sposób osiągnięto bezpieczeństwo wątków w tej klasie zmiennych; przez każdy wątek działa z własną instancją mutable, ale z tą samą instancją ThreadLocal. Oznacza, że cały wątek będzie współdzielił tę samą instancję ThreadLocal jako klucz, ale inną instancję SimpleDateFormat jako wartość.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
źródło
Zmienne lokalne wątków są często używane, aby zapobiec współużytkowaniu w projektach opartych na zmiennych Singletonach lub zmiennych globalnych.
Może być używany w scenariuszach takich jak nawiązywanie oddzielnego połączenia JDBC dla każdego wątku, gdy nie używasz puli połączeń.
Wywołanie getConnection spowoduje zwrócenie połączenia powiązanego z tym wątkiem. To samo można zrobić z innymi właściwościami, takimi jak dateformat, kontekst transakcji, którego nie chcesz udostępniać między wątkami.
Mogłeś również użyć zmiennych lokalnych do tego samego, ale te zasoby zwykle zajmują dużo czasu podczas tworzenia, więc nie chcesz ich tworzyć ponownie za każdym razem, gdy wykonujesz z nimi logikę biznesową. Jednak wartości ThreadLocal są przechowywane w samym obiekcie wątku i gdy tylko wątek zostanie wyrzucony, elementy te również znikają.
Ten link bardzo dobrze wyjaśnia użycie ThreadLocal.
źródło
ThreadLocal klasa w Java umożliwia tworzenie zmiennych, które mogą być odczytywane i zapisywane przez tego samego wątku tylko. Dlatego nawet jeśli dwa wątki wykonują ten sam kod, a kod zawiera odwołanie do zmiennej ThreadLocal, wówczas oba wątki nie widzą nawzajem swoich zmiennych ThreadLocal.
Czytaj więcej
źródło
Istnieją 3 scenariusze korzystania z pomocnika klasy, takiego jak SimpleDateFormat w kodzie wielowątkowym, z których najlepszym jest użycie ThreadLocal
Scenariusze
1- Korzystanie z podobnego obiektu udostępniania za pomocą mechanizmu blokady lub synchronizacji , który spowalnia działanie aplikacji
2- Używanie jako obiektu lokalnego w metodzie
W tym scenariuszu, jeśli mamy 4 wątki, każdy wywołuje metodę 1000 razy, to mamy
4000 obiektów SimpleDateFormat utworzonych i oczekujących na ich usunięcie przez GC
3- Korzystanie z ThreadLocal
jeśli mamy 4 wątki i podaliśmy każdemu wątkowi jedną instancję SimpleDateFormat,
więc mamy 4 wątki , 4 obiekty SimpleDateFormat.
Nie ma potrzeby blokowania mechanizmu oraz tworzenia i niszczenia obiektów. (Dobra złożoność czasu i złożoność przestrzeni)
źródło
[Dla odniesienia] ThreadLocal nie może rozwiązać problemów z aktualizacją współdzielonego obiektu. Zalecane jest użycie obiektu staticThreadLocal, który jest wspólny dla wszystkich operacji w tym samym wątku. [Obowiązkowe] metoda remove () musi być zaimplementowana przez zmienne ThreadLocal, szczególnie przy użyciu pul wątków, w których wątki są często ponownie wykorzystywane. W przeciwnym razie może to wpłynąć na późniejszą logikę biznesową i spowodować nieoczekiwane problemy, takie jak wyciek pamięci.
źródło
Buforowanie, czasami musisz obliczyć tę samą wartość dużo czasu, więc przechowując ostatni zestaw danych wejściowych w metodzie, a wynik możesz przyspieszyć kod. Używając Thread Local Storage, nie musisz myśleć o blokowaniu.
źródło
ThreadLocal jest specjalnie wyposażoną funkcją JVM, która zapewnia izolowane miejsce do przechowywania tylko dla wątków. podobnie jak wartość zmiennej o zasięgu instancji są powiązane tylko z danym wystąpieniem klasy. każdy obiekt ma swoje jedyne wartości i nie mogą widzieć siebie nawzajem. podobnie jest koncepcja zmiennych ThreadLocal, są one lokalne dla wątku w sensie instancji obiektu inny wątek, z wyjątkiem tego, który go utworzył, nie może go zobaczyć. Spójrz tutaj
źródło
Threadlocal zapewnia bardzo łatwy sposób na ponowne użycie obiektów przy zerowym koszcie.
Miałem sytuację, w której wiele wątków tworzyło obraz modyfikowalnej pamięci podręcznej przy każdym powiadomieniu o aktualizacji.
Użyłem Threadlocal dla każdego wątku, a następnie każdy wątek musiałby po prostu zresetować stary obraz, a następnie zaktualizować go ponownie z pamięci podręcznej przy każdym powiadomieniu o aktualizacji.
Zwykłe obiekty wielokrotnego użytku z pul obiektów są powiązane z kosztami bezpieczeństwa wątków, podczas gdy takie podejście nie ma żadnego.
źródło
Wypróbuj ten mały przykład, aby poznać zmienną ThreadLocal:
Dane wyjściowe konsoli, jeśli wątek BookA zostanie wykonany jako pierwszy:
Wynik BookA: „słowo A1, słowo A2, słowo A3”.
Wynik BookB: „słowo B1, słowo B2”.
Dane wyjściowe konsoli, jeśli wątek BookB zostanie zrobiony jako pierwszy:
Wynik BookB: 'wordB1, wordB2'.
Księga wyników A: „słowo A1, słowo A2, słowo A3”.
źródło