Objetivo do ThreadLocal?

Duplicação possível: quando e como devo usar uma variável Threadlocal


A finalidade do ThreadLocal, conforme fornecido aqui, afirma que a variável é local para qualquer Thread que acessa um object que contém a variável ThreadLocal. Que diferença faz, em ter uma variável ThreadLocal como membro de uma class e, em seguida, torná-lo local para um segmento, em vez de ter uma variável local para o próprio segmento?

Um segmento é uma unidade de execução e, portanto, vários segmentos podem executar o mesmo código ao mesmo tempo. Se vários segmentos forem executados em um object / instância ao mesmo tempo, eles compartilharão as variables ​​da instância. Cada thread terá suas próprias variables ​​locais, mas é difícil compartilhá-las entre objects sem passar parâmetros.

É melhor explicado por meio de um exemplo. Digamos que você tenha um Servlet que obtenha o usuário conectado e execute algum código.

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

Agora, o que acontece se os methods doSomething () precisarem de access ao object do usuário? Você não pode tornar o object do usuário uma instância ou variável estática porque cada thread usará o mesmo object de usuário. Você poderia passar o object de usuário como um parâmetro, mas isso rapidamente se torna confuso e vaza objects de usuário em todas as chamadas de método:

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

Uma solução mais elegante é colocar o object de usuário em um ThreadLocal

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

Agora, qualquer código que exija o object de usuário a qualquer momento pode obtê-lo, extraindo-o do segmento local, sem precisar recorrer a esses parâmetros extras desagradáveis:

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

Se você usar essa abordagem, lembre-se de remover os objects novamente em um bloco final. Caso contrário, o object do usuário pode ficar em ambientes que usam um Pool de segmentos (como o servidor de aplicativos Tomcat).

Editar: o código para a class estática

 class StaticClass { static private ThreadLocal threadLocal = new ThreadLocal(); static ThreadLocal getThreadLocal() { return threadLocal; } } 

Você tem que perceber que uma instância de uma class que estende Thread não é a mesma coisa que um thread Java real (que pode ser imaginado como um “ponteiro de execução” que executa seu código e o executa).

As instâncias de tal class representam um encadeamento Java e permitem manipulá-lo (por exemplo, interrompê-lo), mas além disso, são apenas objects regulares e seus membros podem ser acessados ​​de todos os encadeamentos que podem obter uma referência ao object ( o que não é difícil ).

É claro que você pode tentar manter um membro privado e certificar-se de que ele seja usado apenas pelos methods run() ou chamados (methods públicos também podem ser chamados de outros tópicos), mas isso é propenso a erros e não é realmente viável para um sistema mais complexo, no qual você não deseja manter todos os dados em uma subclass Thread (na verdade, você não deve subclassificar Thread, mas usar Runnable).

O ThreadLocal é uma maneira simples e flexível de ter dados por thread que não podem ser acessados ​​simultaneamente por outros threads, sem exigir grande esforço ou comprometimento de design.

Um object Thread pode ter membros de dados internos, mas eles podem ser acessados ​​por qualquer pessoa que tenha (ou possa obter) uma referência ao object Thread. Um ThreadLocal é deliberadamente associado apenas a cada Thread que o acessa. A vantagem é que não há problemas de simultaneidade (dentro do contexto do ThreadLocal). Um membro de dados interno do Thread tem todos os mesmos problemas de simultaneidade que qualquer estado compartilhado.

Deixe-me explicar a ideia de associar um resultado a um determinado tópico. A essência de um ThreadLocal é algo assim:

 public class MyLocal { private final Map values = new HashMap(); public T get() { return values.get(Thread.currentThread()); } public void set(T t) { values.put(Thread.currentThread(), t); } } 

Agora há mais do que isso, mas como você pode ver, o valor retornado é determinado pelo thread atual. É por isso que é local para cada thread.

ThreadLocal é muito útil em aplicações web. O padrão típico é que em algum lugar no início do processamento de um estado de solicitação da Web (geralmente em um filtro de servlet) é armazenado em uma variável ThreadLocal. Como todo o processamento de uma solicitação é feito em 1 thread, todos os componentes que participam da solicitação têm access a essa variável.

Há uma input da Wikipedia sobre essa área problemática. Em nosso ambiente, geralmente é usado para manter as coisas locais para solicitar. No lado do servidor, uma solicitação é tratada principalmente por um único encadeamento. Para manter as coisas locais, você coloca seus dados, por exemplo, os dados da session, em uma variável local de thread. Esses dados são invisíveis para a outra solicitação (encadeamentos) e, portanto, você não precisa sincronizá-los com as outras solicitações.

E não se esqueça, existem construções de API JAVA, que não são seguras, por exemplo, DateFormat . Uma instância estática de DateFormat simplesmente não funciona no lado do servidor.

Na verdade, é mais fácil lidar com a programação multiencadeada quando você usa sua própria cópia privada de dados do que com bloqueios e monitores.

A vantagem do ThreadLocals é que eles são utilizáveis ​​por methods executados em Threads de baunilha simples … ou qualquer subclass de Thread.

Por outro lado, se seus locais de thread tiverem que ser implementados como membros de uma subclass customizada de Thread, há muitas coisas que você não pode fazer. Por exemplo, seu aplicativo estará com problemas se precisar executar methods em instâncias de Thread vanilla preexistentes; Exemplo: instâncias que foram criadas por algum código de biblioteca que o gravador do aplicativo não gravou e não pode modificar.