2009-09-29 13 views
28

Możliwy duplikat: When and how should I use a Threadlocal variableCel ThreadLocal?


Celem ThreadLocal jak podano here stanach, że zmienna jest lokalny dla każdego wątku uzyskiwania dostępu do obiektu zawierającego zmienną ThreadLocal. Jaką różnicę robi, mając zmienną ThreadLocal jako członka klasy, a następnie czyniąc ją lokalną w wątku, zamiast mieć lokalną zmienną do samego wątku?

Odpowiedz

71

Wątek jest jednostką wykonawczą, więc wiele wątków może wykonywać ten sam kod w tym samym czasie. Jeśli wiele wątków zostanie wykonanych na obiekcie/instancji w tym samym czasie, udostępnią zmienne instancji. Każdy wątek będzie miał własne zmienne lokalne, ale trudno jest je udostępniać w obiektach bez przekazywania parametrów.

Najlepiej wyjaśnić to na przykładzie. Powiedzmy, że masz Servlet, który pobiera zalogowanego użytkownika, a następnie wykonuje pewien kod.

doGet(HttpServletRequest req, HttpServletResponse resp) { 
    User user = getLoggedInUser(req); 
    doSomething() 
    doSomethingElse() 
    renderResponse(resp) 
} 

Co się stanie, jeśli metody doSomething() będą wymagać dostępu do obiektu użytkownika? Nie można uczynić obiektu użytkownika instancją lub zmienną statyczną, ponieważ każdy wątek będzie używał tego samego obiektu użytkownika. Można przekazać obiekt użytkownika wokół jako parametr, ale ten szybko staje się plączą i przecieki obiektów użytkownika w każdym wywołaniu metoda:

doGet(HttpServletRequest req, HttpServletResponse resp) { 
    User user = getLoggedInUser(req); 
    doSomething(user) 
    doSomethingElse(user) 
    renderResponse(resp,user) 
} 

Bardziej eleganckim rozwiązaniem jest umieszczenie obiektu użytkownika w ThreadLocal

doGet(HttpServletRequest req, HttpServletResponse resp) { 
    User user = getLoggedInUser(req); 
    StaticClass.getThreadLocal().set(user) 
    try { 
    doSomething() 
    doSomethingElse() 
    renderResponse(resp) 
    } 
    finally { 
    StaticClass.getThreadLocal().remove() 
    } 
} 

teraz każdy kod, który wymaga obiektu użytkownika w każdej chwili może zdobyć go przez wyodrębnianie z wątku lokalnej, bez konieczności uciekania się do tych nieznośnych dodatkowych parametrów:

User user = StaticClass.getThreadLocal().get() 

Jeśli użyjesz tego podejścia, pamiętaj, aby ponownie usunąć obiekty w bloku finally. W przeciwnym razie obiekt użytkownika może się kręcić w środowiskach korzystających z puli wątków (np. Serwer aplikacji Tomcat).

Edycja: Kod dla klasy statycznej

class StaticClass { 
    static private ThreadLocal threadLocal = new ThreadLocal<User>(); 

    static ThreadLocal<User> getThreadLocal() { 
    return threadLocal; 
    } 
} 
+1

Co to jest StaticClass? Klasa, która rozszerza wątek? – Ajay

+0

StaticClass to po prostu wygodny sposób na uzyskanie obiektu ThreadLocal. Będę edytować przykład, aby dołączyć kod dla StaticClass. – leonm

+7

Nie powiedziałbym, że zawsze jest bardziej elegancki, ale jest bardziej celowy –

7

Obiekt Thread może zawierać wewnętrzne elementy danych, ale są one dostępne dla każdego, kto ma (lub może uzyskać) odniesienie do obiektu Thread. Obiekt ThreadLocal jest celowo kojarzony tylko z każdym wątkiem, który uzyskuje do niego dostęp. Zaletą jest to, że nie ma problemów z współbieżnością (w kontekście ThreadLocal). Wewnętrzny element danych wątku ma wszystkie te same problemy z współbieżnością, co dowolny stan współużytkowany.

Pozwolę sobie wyjaśnić ideę powiązania wyniku z konkretnym wątkiem. Istotą ThreadLocal jest coś takiego:

public class MyLocal<T> { 
    private final Map<Thread, T> values = new HashMap<Thread, T>(); 

    public T get() { 
    return values.get(Thread.currentThread()); 
    } 

    public void set(T t) { 
    values.put(Thread.currentThread(), t); 
    } 
} 

Teraz chodzi o coś więcej niż tylko to, ale jak widać zwracana wartość jest określana przez obecnego wątku. Dlatego do każdego wątku jest lokalny.

+0

Celowo powiązane z każdym znaczeniem nici ?. Sam wątek jest obiektem w porządku? Tak związany z wątkiem w rzeczywistości oznacza związany z wątku Object? – Ajay

+0

@Ajay: Tak. Instancje ThreadLocal są powiązane z obiektami Thread poprzez pół-prywatne struktury danych dołączone do obiektów Thread; zobacz "paczki prywatne" członkowie Thread.threadLocals i Thread.inherittableThreadLocals –

+0

@ Stephen: Więc jaka to różnica w posiadaniu threadLocal w porównaniu do posiadania klasy Thread. – Ajay

1

Zaletą ThreadLocals jest to, że można ich używać metodami uruchamianymi na zwykłych wątkach wanilii ... lub dowolnej podklasie wątku.

W przeciwieństwie do tego, jeśli lokalni użytkownicy wątku muszą zostać zaimplementowani jako członkowie niestandardowej podklasy wątku, jest wiele rzeczy, których nie można wykonać. Na przykład Twoja aplikacja będzie miała kłopoty, jeśli będzie musiała uruchomić metody na wcześniej istniejących instancjach wątków wanilii; tj. wystąpienia utworzone przez kod biblioteki, który program piszący aplikacji nie zapisał i nie może modyfikować.

5

ThreadLocal jest bardzo przydatna w webapplications. Typowy wzorzec jest taki, że gdzieś na początku przetwarzania żądania WWW (zwykle w filtrze serwletów) stan jest przechowywany w zmiennej ThreadLocal. Ponieważ całe przetwarzanie dla żądania odbywa się w jednym wątku, wszystkie komponenty uczestniczące w żądaniu mają dostęp do tej zmiennej.

2

Istnieje wikipedia entry about ten obszar problemowy. W naszym środowisku jest zwykle używany do przechowywania rzeczy lokalnych. Po stronie serwera żądanie jest obsługiwane głównie przez pojedynczy wątek. Aby zachować aktywność lokalną, umieść swoje dane, np. dane sesji, w zmiennej lokalnej wątku. Te dane są niewidoczne dla drugiego żądania (wątków) i dlatego nie trzeba ich synchronizować z innymi żądaniami.

I nie zapomnij, istnieją konstrukcje API JAVA, które są , a nie bezpieczne dla wątków, np. DateFormat. Statyczne wystąpienie DateFormat po prostu nie działa po stronie serwera.

W rzeczywistości łatwiej jest obsługiwać programowanie wielowątkowe w przypadku korzystania z własnej prywatnej kopii danych niż z obsługą zamków i monitorów.

10

Trzeba zdać sobie sprawę, że instancją klasy, która rozszerza wątek jest nie samo jak rzeczywiste wątku Java (który można sobie wyobrazić jako „wskaźnik wykonania”, która biegnie przez swój kod i uruchamia go).

Wystąpienia takiej klasy reprezentują wątek Java i pozwalają na manipulowanie nim (np. Przerywają go), ale poza tym są zwykłymi obiektami, a ich członkowie mogą uzyskać dostęp ze wszystkich wątków, które mogą uzyskać odniesienie do obiektu (który jest not hard).

Oczywiście możesz spróbować zachować prywatnego członka i upewnić się, że jest używany tylko przez run() lub metody z niego wywoływane (publiczne metody mogą być wywoływane również z innych wątków), ale jest to podatne na błędy, a nie naprawdę możliwe do wykonania w bardziej złożonym systemie, w którym nie chcesz przechowywać wszystkich danych w podklasie Thread (w rzeczywistości nie powinieneś używać podklasy Thread, ale zamiast tego użyj Runnable).

ThreadLocal jest prostym, elastycznym sposobem na uzyskiwanie danych na temat wątków, których nie można uzyskać z równoczesnym dostępem do innych wątków, bez konieczności dużego wysiłku lub kompromisu.