Se você estiver usando o ThreadLocal, nunca escreva um wrapper sobre ele !!! Todo desenvolvedor que deseja usar a variável deve saber que é 'ThreadLocal'
Não é um bug
2
@ Nototug Se você é explícito, pode nomear sua variável para que esteja lidando com um valor ThreadLocal, por exemplo RequestUtils.getCurrentRequestThreadLocal(). Não dizendo que isso é muito elegante, mas isso decorre do fato de o ThreadLocal em si não ser muito elegante na maioria dos casos.
whirlwin
@Sasha Pode o ThreadLocal ser usado para rastrear a execução loja
gaurav
Respostas:
864
Um uso possível (e comum) é quando você tem algum objeto que não é seguro para threads, mas deseja evitar sincronizar o acesso a esse objeto (estou olhando para você, SimpleDateFormat ). Em vez disso, dê a cada thread sua própria instância do objeto.
Por exemplo:
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);}}
Outra alternativa à sincronização ou ao threadlocal é tornar a variável uma variável local. Vars locais são sempre thread-safe. Suponho que é uma prática inadequada tornar os DateFormats locais porque eles são caros de criar, mas nunca vi métricas sólidas sobre esse tópico.
@overthink Existe alguma razão para declarar ThreadLocal estático e final, quero dizer desempenho ou algo assim?
Sachin Gorade
4
O método ThreadLocal.get () chamará ThreadLocal.initialValue () (uma vez) para cada segmento, o que significa que um objeto SimpleDateFormat é criado para cada segmento. Não é melhor usar o SimpleDateFormat como variáveis locais (já que não precisamos lidar com problemas de coleta de lixo)?
sudeepdino008
3
Apenas tome cuidado para não usar a inicialização entre chaves para seu SimpleDateFormat, pois isso criaria uma classe anônima para que seu carregador de classes não pudesse ser coletado como lixo. vazamento de memória: retornar novo SimpleDateFormat () {{applyPattern ("aaaaMMdd HHmm")}};
user1944408
427
Como a ThreadLocalé uma referência aos dados em um determinado Thread, você pode acabar com vazamentos de carregamento de classe ao usar ThreadLocals em servidores de aplicativos usando conjuntos de encadeamentos. Você precisa ter muito cuidado com a limpeza de quaisquer ThreadLocals você get()ou set()usando o ThreadLocal's remove()método.
Se você não fizer a limpeza quando terminar, quaisquer referências que ele contenha para classes carregadas como parte de um webapp implantado permanecerão no heap permanente e nunca serão coletadas como lixo. A reimplantação / desimplantação do aplicativo da web não limpará a Threadreferência de cada uma das classes do aplicativo da web, uma vez que Threadnão é algo de propriedade do seu aplicativo da web. Cada implantação sucessiva criará uma nova instância da classe que nunca será coletada como lixo.
Você terminará com exceções de falta de memória devido a java.lang.OutOfMemoryError: PermGen spacee após alguns estudos no Google provavelmente aumentará em -XX:MaxPermSizevez de corrigir o erro.
Alex Vasseur mudou seu blog. Aqui está um link atual para o artigo sobre vazamento de memória.
Kenster
12
O fio que liga Julien para se mudou para cá que parece, e é bem vale a pena ler ...
Donal Fellows
28
Essa é uma quantidade enorme de votos positivos para uma resposta que, embora informativa, não responde de maneira alguma à pergunta.
22614 Robin
8
Agora que o PermGen é morto pelo Java 8, isso muda essa resposta de alguma maneira?
Máquina de lavar malvada
13
@ Robin: Eu discordo. A questão é sobre como usar o ThreadLocal corretamente, para obter um entendimento completo de qualquer conceito (ThreadLocal aqui), também é importante entender como não usá-lo e os riscos de usá-lo descuidadamente, e é disso que se trata a resposta de Phil. Fico feliz que ele tenha abordado esse ponto que não é abordado em nenhuma outra resposta. Todos esses votos são merecidos. Acredito que o SO deve construir uma compreensão dos conceitos, em vez de apenas ser um site de controle de qualidade.
Saurabh Patil
166
Muitas estruturas usam ThreadLocals para manter algum contexto relacionado ao segmento atual. Por exemplo, quando a transação atual é armazenada em um ThreadLocal, você não precisa transmiti-la como parâmetro em todas as chamadas de métodos, caso alguém da pilha precise acessar a mesma. Os aplicativos da Web podem armazenar informações sobre a solicitação e sessão atuais em um ThreadLocal, para que o aplicativo tenha acesso fácil a eles. Com o Guice, você pode usar o ThreadLocals ao implementar escopos personalizados para os objetos injetados (o padrão do Guice escopos de servlet provavelmente os usam também).
ThreadLocals são um tipo de variáveis globais (embora um pouco menos ruins porque estão restritas a um thread), portanto, você deve ter cuidado ao usá-las para evitar efeitos colaterais indesejados e vazamentos de memória. Crie suas APIs para que os valores ThreadLocal sempre sejam limpos automaticamente quando não forem mais necessários e que o uso incorreto da API não seja possível (por exemplo, assim ). O ThreadLocals pode ser usado para tornar o código mais limpo e, em alguns casos raros, é a única maneira de fazer alguma coisa funcionar (meu projeto atual teve dois desses casos; eles estão documentados aqui em "Campos estáticos e variáveis globais").
É exatamente assim que a estrutura exPOJO (www.expojo.com) permite acesso ao ORM Session / PersistenceManager sem precisar da sobrecarga de anotações e injeção. É como 'injeção de thread' em vez de 'injeção de objeto'. Ele fornece acesso a dependências sem a necessidade (e a sobrecarga) de tê-las incorporadas em todos os objetos que possam precisar dessas dependências. É incrível como 'peso leve' você pode fazer um quadro DI quando você usa injeção de rosca em vez de DI clássico (por exemplo, Primavera etc,.)
Volksman
27
Por que tive que rolar até agora para encontrar esta resposta essencial !?
9608 Jeremy Stein
1
@Esko, em seu projeto, o uso de ThreadLocal em hashcode e igual é desnecessário. Uma abordagem melhor é criar ou passar uma referência a uma função de código de hash / igual sempre que necessário. Dessa forma, você pode evitar completamente a necessidade do hack ThreadLocal.
Em Java, se você possui um dado que pode variar por segmento, suas opções são passá-lo a todos os métodos que precisam (ou podem precisar) dele ou associar o dado ao segmento. Passar o dado por todo o lado pode ser viável se todos os seus métodos já precisarem passar por uma variável "contexto" comum.
Se não for esse o caso, convém não desorganizar as assinaturas do método com um parâmetro adicional. Em um mundo não encadeado, você pode resolver o problema com o equivalente em Java de uma variável global. Em uma palavra encadeada, o equivalente a uma variável global é uma variável local de encadeamento.
Portanto, você deve evitar os locais de threads da mesma maneira que evita os globais. Não posso aceitar que não há problema em criar globais (threads locais) em vez de transmitir valores, as pessoas não gostam, porque muitas vezes revela problemas de arquitetura que não desejam corrigir.
Juan Mendes
10
Talvez ... Pode ser útil, no entanto, se você tiver uma enorme base de código existente na qual precisará adicionar um novo dado que deve ser distribuído por toda parte , por exemplo, um contexto de sessão, transação db, usuário conectado, etc. .
Cornel Masson
6
Parabéns por usar dados em vez de dados.
profunda
Isso me deixou curioso. 'datum' is the singular form and 'data' is the plural form.
Siddhartha
23
Há um exemplo muito bom no livro Java Concurrency in Practice . Onde o autor ( Joshua Bloch ) explica como o confinamento do Thread é uma das maneiras mais simples de obter segurança de thread e o ThreadLocal é o meio mais formal de manter o confinamento do thread. No final, ele também explica como as pessoas podem abusar dele usando-o como variáveis globais.
Copiei o texto do livro mencionado, mas o código 3.10 está ausente, pois não é muito importante entender onde o ThreadLocal deve ser usado.
As variáveis locais de encadeamento são frequentemente usadas para impedir o compartilhamento em projetos baseados em Singletons mutáveis ou variáveis globais. Por exemplo, um aplicativo de thread único pode manter uma conexão de banco de dados global inicializada na inicialização para evitar a necessidade de passar uma Conexão para todos os métodos. Como as conexões JDBC podem não ser seguras para threads, um aplicativo multithread que usa uma conexão global sem coordenação adicional também não é seguro para threads. Usando um ThreadLocal para armazenar a conexão JDBC, como no ConnectionHolder na Listagem 3.10, cada encadeamento terá sua própria conexão.
O ThreadLocal é amplamente usado na implementação de estruturas de aplicativos. Por exemplo, os contêineres J2EE associam um contexto de transação a um encadeamento em execução pela duração de uma chamada EJB. Isso é facilmente implementado usando um Thread-Local estático que mantém o contexto da transação: quando o código da estrutura precisa determinar qual transação está em execução no momento, ele busca o contexto da transação neste ThreadLocal. Isso é conveniente, pois reduz a necessidade de passar informações de contexto de execução para todos os métodos, mas associa qualquer código que use esse mecanismo à estrutura.
É fácil abusar do ThreadLocal, tratando sua propriedade de confinamento de threads como uma licença para usar variáveis globais ou como um meio de criar argumentos de método "ocultos". Como as variáveis globais, as variáveis locais do encadeamento podem prejudicar a reutilização e introduzir acoplamentos ocultos entre as classes e, portanto, devem ser usadas com cuidado.
Essencialmente, quando você precisa que o valor de uma variável dependa do encadeamento atual e não seja conveniente anexar o valor ao encadeamento de alguma outra maneira (por exemplo, encadeamento de subclassificação).
Um caso típico é onde alguma outra estrutura criou o encadeamento em que seu código está sendo executado, por exemplo, um contêiner de servlet ou onde faz mais sentido usar ThreadLocal porque sua variável está "em seu lugar lógico" (em vez de uma variável pendurado em uma subclasse Thread ou em outro mapa de hash).
Algumas pessoas defendem o uso do ThreadLocal como uma maneira de anexar um "ID do thread" a cada thread em certos algoritmos simultâneos em que você precisa de um número de thread (consulte, por exemplo, Herlihy & Shavit). Nesses casos, verifique se você está realmente recebendo um benefício!
Pode o ThreadLocal ser usado para rastrear a execução loja
gaurav
13
O ThreadLocal em Java foi introduzido no JDK 1.2, mas mais tarde foi gerado no JDK 1.5 para introduzir segurança de tipo na variável ThreadLocal.
O ThreadLocal pode ser associado ao escopo do Thread, todo o código executado pelo Thread tem acesso às variáveis ThreadLocal, mas dois threads não podem ver a variável ThreadLocal um do outro.
Cada encadeamento contém uma cópia exclusiva da variável ThreadLocal que se torna elegível para a coleta de lixo após o encadeamento terminar ou morrer, normalmente ou devido a qualquer exceção, considerando que a variável ThreadLocal não possui outras referências ativas.
As variáveis ThreadLocal em Java geralmente são campos estáticos privados em Classes e mantêm seu estado dentro do Thread.
Pode o ThreadLocal ser usado para rastrear a execução loja
gaurav
11
A documentação diz muito bem: "cada thread que acessa [uma variável local de thread] (por meio de seu método get ou set) possui sua própria cópia inicializada da variável, independente".
Você usa um quando cada segmento deve ter sua própria cópia de algo. Por padrão, os dados são compartilhados entre os threads.
Por padrão, objetos estáticos, ou objetos explicitamente transmitidos entre threads, nos quais os dois threads possuem uma referência ao mesmo objeto, são compartilhados. Objetos declarados localmente em um encadeamento não são compartilhados (são locais na pilha do encadeamento). Só queria esclarecer isso.
Ian Varley
@IanVarley: Obrigado por apontar isso. Portanto, se tivermos uma variável declarada e usada na classe MyThread, precisamos do ThreadLocal nesse caso? Exemplo: a classe MyThread estende o Thread {String var1 ;, int var2; } nesse caso, var1 e var2 farão parte da própria pilha do encadeamento e não serão compartilhados. Por isso, exigimos ThreadLocal nesse caso? Eu posso estar totalmente errado na minha compreensão, por favor, guie.
Jayesh
4
Se cada encadeamento deseja ter sua própria cópia de algo, por que não pode simplesmente ser declarado local (que é sempre encadeado)?
sudeepdino008
@ sudeepdino008 você nem sempre tem o controle da criação desses tópicos (estruturas webapp, ThreadPool bibliotecas)
JMess
10
O servidor Webapp pode manter um conjunto de encadeamentos e uma ThreadLocalvar deve ser removida antes da resposta ao cliente, portanto, o encadeamento atual pode ser reutilizado pela próxima solicitação.
Dois casos de uso em que a variável threadlocal pode ser usada -
1- Quando temos um requisito para associar o estado a um encadeamento (por exemplo, um ID do usuário ou Transaction). Isso geralmente acontece com um aplicativo da Web que toda solicitação que vai para um servlet tem um transactionID exclusivo associado a ele.
// 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();}}
Observe que aqui o método withInitial é implementado usando a expressão lambda.
2- Outro caso de uso é quando queremos ter uma instância segura de thread e não queremos usar a sincronização, pois o custo de desempenho com a sincronização é maior. Um desses casos é quando SimpleDateFormat é usado. Como o SimpleDateFormat não é seguro para threads, precisamos fornecer um mecanismo para torná-lo seguro.
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()));}}
Ainda não vi o benefício de usar o ThreadLocal para gerar o ID exclusivo no exemplo um, se todo o seu objetivo for retornar um valor inteiro único incremental. `` public class ThreadId {// Número inteiro atômico que contém o próximo ID do segmento a ser atribuído private static final AtomicInteger nextId = new AtomicInteger (0); // Retorna o ID exclusivo do encadeamento atual, atribuindo-o, se necessário, public static int get () {return nextId..getAndIncrement (); }} `` `
dashenswen 25/04/19
7
Desde o lançamento do Java 8, há uma maneira mais declarativa de inicializar ThreadLocal:
ThreadLocal<Cipher> local =ThreadLocal.withInitial(()->"init value");
Até o lançamento do Java 8, você precisava fazer o seguinte:
ThreadLocal<String> local =newThreadLocal<String>(){@OverrideprotectedString initialValue(){return"init value";}};
Além disso, se o método de instanciação (construtor, método de fábrica) da classe usada para ThreadLocalnão aceitar nenhum parâmetro, você poderá simplesmente usar referências de método (introduzidas no Java 8):
classNotThreadSafe{// no parameterspublicNotThreadSafe(){}}ThreadLocal<NotThreadSafe> container =ThreadLocal.withInitial(NotThreadSafe::new);
Nota: A
avaliação é lenta, pois você está passando o java.util.function.Supplierlambda que é avaliado apenas quando ThreadLocal#geté chamado, mas o valor não foi avaliado anteriormente.
Você precisa ter muito cuidado com o padrão ThreadLocal. Existem algumas desvantagens importantes, como Phil mencionou, mas uma que não foi mencionada é garantir que o código que configura o contexto ThreadLocal não seja "reentrante".
Coisas ruins podem acontecer quando o código que define as informações é executado uma segunda ou terceira vez, porque as informações no seu encadeamento podem começar a sofrer alterações quando você não esperava. Portanto, verifique se as informações do ThreadLocal não foram definidas antes de defini-las novamente.
A reentrada não é um problema se o código estiver preparado para lidar com isso. Na entrada, anote se a variável já está definida e, na saída, restaure seu valor anterior (se houver) ou remova-o (se não).
Supercat
1
@ Jeff, Cara, isso é verdade para todo código que você escreve, não apenas para o ThreadLocalpadrão. Se você fizer F(){ member=random(); F2(); write(member); }e F2 substituir o membro por um novo valor, obviamente write(member)não gravará mais o número que você random()editou. Isso é literalmente apenas senso comum. Da mesma forma, se o fizer F(){ F(); }, então dê sorte com seu loop infinito! Isso é verdade em todos os lugares e não é específico ThreadLocal.
Pacerier 19/09/17
@ Jeff, exemplo de código?
Pacerier
5
quando?
Quando um objeto não é seguro para threads, em vez da sincronização que dificulta a escalabilidade, atribua um objeto a cada thread e mantenha-o no escopo do thread, que é ThreadLocal. Um dos objetos usados com mais freqüência, mas não com segurança de encadeamento, é o banco de dados Connection e JMSConnection.
Como ?
Um exemplo é o framework Spring que usa o ThreadLocal fortemente para gerenciar transações nos bastidores, mantendo esses objetos de conexão nas variáveis ThreadLocal. Em um nível alto, quando uma transação é iniciada, ela obtém a conexão (e desativa a confirmação automática) e a mantém em ThreadLocal. em chamadas db adicionais, ele usa a mesma conexão para se comunicar com db. No final, ele pega a conexão do ThreadLocal e confirma (ou reverte) a transação e libera a conexão.
Acho que o log4j também usa o ThreadLocal para manter o MDC.
ThreadLocal é útil quando você deseja ter algum estado que não deve ser compartilhado entre diferentes threads, mas deve ser acessível a partir de cada thread durante toda a sua vida útil.
Como exemplo, imagine um aplicativo Web, em que cada solicitação é atendida por um encadeamento diferente. Imagine que, para cada solicitação, você precise de um pedaço de dados várias vezes, o que é bastante caro para calcular. No entanto, esses dados podem ter sido alterados para cada solicitação recebida, o que significa que você não pode usar um cache simples. Uma solução simples e rápida para esse problema seria ter uma ThreadLocalvariável mantendo acesso a esses dados, para que você precise calculá-los apenas uma vez para cada solicitação. Obviamente, esse problema também pode ser resolvido sem o uso deThreadLocal , mas eu o projetei para fins ilustrativos.
Dito isto, tenha em mente que ThreadLocals são essencialmente uma forma de estado global. Como resultado, ele tem muitas outras implicações e deve ser usado somente após considerar todas as outras soluções possíveis.
ThreadLocals NÃO são um estado global, a menos que você o torne um estado global. Eles são praticamente sempre recursos de pilha acessíveis. Você pode simular o encadeamento local simplesmente passando essa variável em todos os seus métodos (que é retardado); que não torná-lo estado global ...
Enerccio
É uma forma de estado global, no sentido em que é acessível de qualquer lugar do código (no mesmo contexto do thread). Isso vem com todas as repercussões, como não poder argumentar sobre quem lê e grava esse valor. Usar um parâmetro de função não é algo retardado, é uma boa prática promover interfaces limpas. No entanto, eu concordo com você que passar um parâmetro por toda a profundidade da sua base de código é um cheiro de código. Mas também acredito que, em muitas ocasiões, o uso do ThreadLocal é um cheiro de código em primeiro lugar que o levou até aqui, então é isso que se deve reconsiderar.
Dimos
Pode simplesmente fazer parte do estado do objeto, não precisa ser usado globalmente. Claro, você começa a sobrecarga pouco pouco, mas se vários objetos tem que ter estado diferente por thread, você pode usar ThreadLocalcomo campo de objeto ...
Enerccio
Você está certo. Eu provavelmente me deparei com vários usos indevidos de ThreadLocal, onde era acessível globalmente. Como você disse, ainda pode ser usado como um campo de classe que limita a visibilidade.
Dimos
4
Nada realmente novo aqui, mas descobri hoje que ThreadLocalé muito útil ao usar a Validação de Bean em um aplicativo da web. As mensagens de validação são localizadas, mas por padrão são usadas Locale.getDefault(). Você pode configurar o Validatorcom um diferente MessageInterpolator, mas não há como especificar o Localequando você chama validate. Portanto, você pode criar uma estática ThreadLocal<Locale>(ou, melhor ainda, um contêiner geral com outras coisas que talvez você precise ser ThreadLocale, em seguida, MessageInterpolatorescolher o Localeque é personalizado. O próximo passo é escrever um ServletFilterque use um valor de sessão ou request.getLocale()escolher o local e armazenar no seuThreadLocal referência.
Como foi mencionado por @unknown (google), seu uso é definir uma variável global na qual o valor referenciado pode ser único em cada thread. Seus usos normalmente envolvem o armazenamento de algum tipo de informação contextual vinculada ao encadeamento atual de execução.
Nós o usamos em um ambiente Java EE para passar a identidade do usuário para classes que não têm conhecimento do Java EE (não têm acesso ao HttpSession ou ao EJB SessionContext). Dessa forma, o código, que faz uso da identidade para operações baseadas em segurança, pode acessar a identidade de qualquer lugar, sem ter que passar explicitamente em todas as chamadas de método.
O ciclo de operações de solicitação / resposta na maioria das chamadas Java EE facilita esse tipo de uso, pois fornece pontos de entrada e saída bem definidos para definir e desabilitar o ThreadLocal.
O ThreadLocal garantirá que o acesso ao objeto mutável pelos vários threads no método não sincronizado seja sincronizado, significa tornar o objeto mutável imutável no método.
Isso é obtido dando uma nova instância de objeto mutável para cada thread que tenta acessá-lo. Portanto, é uma cópia local para cada segmento. Isso é um truque para tornar a variável de instância em um método a ser acessado como uma variável local. Como você sabe que a variável local do método está disponível apenas para o encadeamento, uma diferença é; As variáveis locais do método não estarão disponíveis para o encadeamento quando a execução do método terminar, onde o objeto mutável compartilhado com o threadlocal estará disponível em vários métodos até que seja limpo.
Por definição:
A classe ThreadLocal em Java permite criar variáveis que podem ser lidas e gravadas apenas pelo mesmo encadeamento. Portanto, mesmo que dois threads estejam executando o mesmo código e o código tenha uma referência a uma variável ThreadLocal, os dois threads não poderão ver as variáveis ThreadLocal um do outro.
Cada um Threadem java contém ThreadLocalMapnele.
Onde
Key=OneThreadLocal object shared across threads.
value =Mutable object which has to be used synchronously,this will be instantiated for each thread.
Atingindo o ThreadLocal:
Agora crie uma classe de wrapper para o ThreadLocal que manterá o objeto mutável como abaixo (com ou sem initialValue()). Agora, o getter e o setter deste wrapper funcionarão na instância threadlocal em vez de no objeto mutável.
Se getter () de threadlocal não encontrou nenhum valor no threadlocalmap do Thread; em seguida, ele chamará o initialValue () para obter sua cópia particular em relação ao encadeamento.
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();}}
Agora wrapper.getDateFormatter()chamará threadlocal.get()e isso verificará a instância currentThread.threadLocalMapcontém esta (threadlocal).
Se sim, retorne o valor (SimpleDateFormat) para a instância threadlocal correspondente, caso
contrário, adicione o mapa com essa instância threadlocal, initialValue ().
Com isso, a segurança do thread é alcançada nessa classe mutável; por cada thread está trabalhando com sua própria instância mutável, mas com a mesma instância ThreadLocal. Significa que todo o encadeamento compartilhará a mesma instância ThreadLocal da chave, mas diferentes instâncias SimpleDateFormat como valor.
As variáveis locais de encadeamento são frequentemente usadas para impedir o compartilhamento em projetos baseados em Singletons mutáveis ou variáveis globais.
Ele pode ser usado em cenários como fazer uma conexão JDBC separada para cada encadeamento quando você não estiver usando um Conjunto de Conexões.
Quando você chama getConnection, ele retornará uma conexão associada a esse thread. O mesmo pode ser feito com outras propriedades como dateformat, contexto de transação que você não deseja compartilhar entre threads.
Você também pode ter usado variáveis locais para o mesmo, mas esses recursos geralmente demoram na criação, portanto, você não deseja criá-las repetidamente sempre que executar alguma lógica de negócios com elas. No entanto, os valores ThreadLocal são armazenados no próprio objeto de encadeamento e, assim que o encadeamento é coletado de lixo, esses valores desaparecem também.
Neste exemplo, há uma questão importante: quem é o responsável pelo fechamento da conexão? Em vez de criar esta conexão como valor inicial, deixe o consumidor criar a conexão explicitamente e vinculá-la ao thread via ThreadLocal. O criador da conexão também é responsável pelo fechamento. Isso não está claro neste exemplo. Criar e vincular a conexão também pode ser oculto em uma estrutura simples por algo como Transaction.begin () e Transaction.end ().
Rick-Rainer Ludwig,
2
A classe ThreadLocal em Java permite criar variáveis que podem ser lidas e gravadas apenas pelo mesmo encadeamento. Portanto, mesmo que dois threads estejam executando o mesmo código e o código tenha uma referência a uma variável ThreadLocal, os dois threads não poderão ver as variáveis ThreadLocal um do outro.
Existem três cenários para usar um auxiliar de classe como SimpleDateFormat no código multithread, o melhor é usar ThreadLocal
Cenários
1- Usando o objeto de compartilhamento com a ajuda do mecanismo de bloqueio ou sincronização , que torna o aplicativo lento
2- Usando como objeto local dentro de um método
Nesse cenário, se tivermos 4 encadeamentos, cada um chamará um método 1000 vezes, teremos 4000 objetos SimpleDateFormat criados e aguardando o GC apagá-los
3- Usando ThreadLocal
se tivermos 4 threads e demos a cada thread uma instância SimpleDateFormat
, teremos 4 threads , 4 objetos de SimpleDateFormat.
Não há necessidade de mecanismo de bloqueio e criação e destruição de objetos. (Boa complexidade de tempo e complexidade de espaço)
[Para referência] O ThreadLocal não pode resolver problemas de atualização do objeto compartilhado. É recomendável usar um objeto staticThreadLocal que seja compartilhado por todas as operações no mesmo encadeamento. O método [Obrigatório] remove () deve ser implementado pelas variáveis ThreadLocal, especialmente ao usar conjuntos de encadeamentos nos quais os encadeamentos geralmente são reutilizados. Caso contrário, isso pode afetar a lógica comercial subsequente e causar problemas inesperados, como vazamento de memória.
Armazenamento em cache, em algum momento você deve calcular o mesmo valor por muito tempo, armazenando o último conjunto de entradas em um método e o resultado pode acelerar o código. Ao usar o Thread Local Storage, você evita pensar em travar.
ThreadLocal é uma funcionalidade especialmente provisionada pela JVM para fornecer um espaço de armazenamento isolado apenas para threads. como o valor da variável com escopo da instância, são vinculados a uma determinada instância apenas de uma classe. cada objeto tem seus únicos valores e eles não podem se ver. assim como o conceito de variáveis ThreadLocal, elas são locais para o segmento, no sentido de instâncias de objeto, outro segmento, exceto aquele que o criou, não pode vê-lo. Veja aqui
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();}}
O Threadlocal fornece uma maneira muito fácil de obter a reutilização de objetos com custo zero.
Eu tive uma situação em que vários threads estavam criando uma imagem de cache mutável, em cada notificação de atualização.
Usei um Threadlocal em cada thread e, em seguida, cada thread precisaria redefinir a imagem antiga e atualizá-la novamente a partir do cache em cada notificação de atualização.
Objetos reutilizáveis comuns de conjuntos de objetos têm um custo de segurança de encadeamento associado a eles, enquanto essa abordagem não possui nenhum.
RequestUtils.getCurrentRequestThreadLocal()
. Não dizendo que isso é muito elegante, mas isso decorre do fato de o ThreadLocal em si não ser muito elegante na maioria dos casos.Respostas:
Um uso possível (e comum) é quando você tem algum objeto que não é seguro para threads, mas deseja evitar sincronizar o acesso a esse objeto (estou olhando para você, SimpleDateFormat ). Em vez disso, dê a cada thread sua própria instância do objeto.
Por exemplo:
Documentação .
fonte
SimpleDateFormat
. Talvez seja melhor usar uma alternativa segura para threads . Se você concorda que singletons são ruins ,ThreadLocal
é ainda pior.Como a
ThreadLocal
é uma referência aos dados em um determinadoThread
, você pode acabar com vazamentos de carregamento de classe ao usarThreadLocal
s em servidores de aplicativos usando conjuntos de encadeamentos. Você precisa ter muito cuidado com a limpeza de quaisquerThreadLocal
s vocêget()
ouset()
usando oThreadLocal
'sremove()
método.Se você não fizer a limpeza quando terminar, quaisquer referências que ele contenha para classes carregadas como parte de um webapp implantado permanecerão no heap permanente e nunca serão coletadas como lixo. A reimplantação / desimplantação do aplicativo da web não limpará a
Thread
referência de cada uma das classes do aplicativo da web, uma vez queThread
não é algo de propriedade do seu aplicativo da web. Cada implantação sucessiva criará uma nova instância da classe que nunca será coletada como lixo.Você terminará com exceções de falta de memória devido a
java.lang.OutOfMemoryError: PermGen space
e após alguns estudos no Google provavelmente aumentará em-XX:MaxPermSize
vez de corrigir o erro.Se você enfrentar esses problemas, poderá determinar qual thread e classe está mantendo essas referências usando o Memory Analyzer do Eclipse e / ou seguindo o guia e acompanhamento de Frank Kieviet .
Atualização: redescobri a entrada do blog de Alex Vasseur que me ajudou a rastrear alguns
ThreadLocal
problemas que eu estava tendo.fonte
Muitas estruturas usam ThreadLocals para manter algum contexto relacionado ao segmento atual. Por exemplo, quando a transação atual é armazenada em um ThreadLocal, você não precisa transmiti-la como parâmetro em todas as chamadas de métodos, caso alguém da pilha precise acessar a mesma. Os aplicativos da Web podem armazenar informações sobre a solicitação e sessão atuais em um ThreadLocal, para que o aplicativo tenha acesso fácil a eles. Com o Guice, você pode usar o ThreadLocals ao implementar escopos personalizados para os objetos injetados (o padrão do Guice escopos de servlet provavelmente os usam também).
ThreadLocals são um tipo de variáveis globais (embora um pouco menos ruins porque estão restritas a um thread), portanto, você deve ter cuidado ao usá-las para evitar efeitos colaterais indesejados e vazamentos de memória. Crie suas APIs para que os valores ThreadLocal sempre sejam limpos automaticamente quando não forem mais necessários e que o uso incorreto da API não seja possível (por exemplo, assim ). O ThreadLocals pode ser usado para tornar o código mais limpo e, em alguns casos raros, é a única maneira de fazer alguma coisa funcionar (meu projeto atual teve dois desses casos; eles estão documentados aqui em "Campos estáticos e variáveis globais").
fonte
Em Java, se você possui um dado que pode variar por segmento, suas opções são passá-lo a todos os métodos que precisam (ou podem precisar) dele ou associar o dado ao segmento. Passar o dado por todo o lado pode ser viável se todos os seus métodos já precisarem passar por uma variável "contexto" comum.
Se não for esse o caso, convém não desorganizar as assinaturas do método com um parâmetro adicional. Em um mundo não encadeado, você pode resolver o problema com o equivalente em Java de uma variável global. Em uma palavra encadeada, o equivalente a uma variável global é uma variável local de encadeamento.
fonte
'datum' is the singular form and 'data' is the plural form.
Há um exemplo muito bom no livro Java Concurrency in Practice . Onde o autor ( Joshua Bloch ) explica como o confinamento do Thread é uma das maneiras mais simples de obter segurança de thread e o ThreadLocal é o meio mais formal de manter o confinamento do thread. No final, ele também explica como as pessoas podem abusar dele usando-o como variáveis globais.
Copiei o texto do livro mencionado, mas o código 3.10 está ausente, pois não é muito importante entender onde o ThreadLocal deve ser usado.
fonte
Essencialmente, quando você precisa que o valor de uma variável dependa do encadeamento atual e não seja conveniente anexar o valor ao encadeamento de alguma outra maneira (por exemplo, encadeamento de subclassificação).
Um caso típico é onde alguma outra estrutura criou o encadeamento em que seu código está sendo executado, por exemplo, um contêiner de servlet ou onde faz mais sentido usar ThreadLocal porque sua variável está "em seu lugar lógico" (em vez de uma variável pendurado em uma subclasse Thread ou em outro mapa de hash).
No meu site, tenho mais algumas discussões e exemplos de quando usar o ThreadLocal que também podem ser interessantes.
Algumas pessoas defendem o uso do ThreadLocal como uma maneira de anexar um "ID do thread" a cada thread em certos algoritmos simultâneos em que você precisa de um número de thread (consulte, por exemplo, Herlihy & Shavit). Nesses casos, verifique se você está realmente recebendo um benefício!
fonte
O ThreadLocal em Java foi introduzido no JDK 1.2, mas mais tarde foi gerado no JDK 1.5 para introduzir segurança de tipo na variável ThreadLocal.
O ThreadLocal pode ser associado ao escopo do Thread, todo o código executado pelo Thread tem acesso às variáveis ThreadLocal, mas dois threads não podem ver a variável ThreadLocal um do outro.
Cada encadeamento contém uma cópia exclusiva da variável ThreadLocal que se torna elegível para a coleta de lixo após o encadeamento terminar ou morrer, normalmente ou devido a qualquer exceção, considerando que a variável ThreadLocal não possui outras referências ativas.
As variáveis ThreadLocal em Java geralmente são campos estáticos privados em Classes e mantêm seu estado dentro do Thread.
Leia mais: ThreadLocal em Java - programa e tutorial de exemplo
fonte
A documentação diz muito bem: "cada thread que acessa [uma variável local de thread] (por meio de seu método get ou set) possui sua própria cópia inicializada da variável, independente".
Você usa um quando cada segmento deve ter sua própria cópia de algo. Por padrão, os dados são compartilhados entre os threads.
fonte
O servidor Webapp pode manter um conjunto de encadeamentos e uma
ThreadLocal
var deve ser removida antes da resposta ao cliente, portanto, o encadeamento atual pode ser reutilizado pela próxima solicitação.fonte
Dois casos de uso em que a variável threadlocal pode ser usada -
1- Quando temos um requisito para associar o estado a um encadeamento (por exemplo, um ID do usuário ou Transaction). Isso geralmente acontece com um aplicativo da Web que toda solicitação que vai para um servlet tem um transactionID exclusivo associado a ele.
Observe que aqui o método withInitial é implementado usando a expressão lambda.
2- Outro caso de uso é quando queremos ter uma instância segura de thread e não queremos usar a sincronização, pois o custo de desempenho com a sincronização é maior. Um desses casos é quando SimpleDateFormat é usado. Como o SimpleDateFormat não é seguro para threads, precisamos fornecer um mecanismo para torná-lo seguro.
fonte
Desde o lançamento do Java 8, há uma maneira mais declarativa de inicializar
ThreadLocal
:Até o lançamento do Java 8, você precisava fazer o seguinte:
Além disso, se o método de instanciação (construtor, método de fábrica) da classe usada para
ThreadLocal
não aceitar nenhum parâmetro, você poderá simplesmente usar referências de método (introduzidas no Java 8):Nota: A avaliação é lenta, pois você está passando o
java.util.function.Supplier
lambda que é avaliado apenas quandoThreadLocal#get
é chamado, mas o valor não foi avaliado anteriormente.fonte
Você precisa ter muito cuidado com o padrão ThreadLocal. Existem algumas desvantagens importantes, como Phil mencionou, mas uma que não foi mencionada é garantir que o código que configura o contexto ThreadLocal não seja "reentrante".
Coisas ruins podem acontecer quando o código que define as informações é executado uma segunda ou terceira vez, porque as informações no seu encadeamento podem começar a sofrer alterações quando você não esperava. Portanto, verifique se as informações do ThreadLocal não foram definidas antes de defini-las novamente.
fonte
ThreadLocal
padrão. Se você fizerF(){ member=random(); F2(); write(member); }
e F2 substituir o membro por um novo valor, obviamentewrite(member)
não gravará mais o número que vocêrandom()
editou. Isso é literalmente apenas senso comum. Da mesma forma, se o fizerF(){ F(); }
, então dê sorte com seu loop infinito! Isso é verdade em todos os lugares e não é específicoThreadLocal
.quando?
Quando um objeto não é seguro para threads, em vez da sincronização que dificulta a escalabilidade, atribua um objeto a cada thread e mantenha-o no escopo do thread, que é ThreadLocal. Um dos objetos usados com mais freqüência, mas não com segurança de encadeamento, é o banco de dados Connection e JMSConnection.
Como ?
Um exemplo é o framework Spring que usa o ThreadLocal fortemente para gerenciar transações nos bastidores, mantendo esses objetos de conexão nas variáveis ThreadLocal. Em um nível alto, quando uma transação é iniciada, ela obtém a conexão (e desativa a confirmação automática) e a mantém em ThreadLocal. em chamadas db adicionais, ele usa a mesma conexão para se comunicar com db. No final, ele pega a conexão do ThreadLocal e confirma (ou reverte) a transação e libera a conexão.
Acho que o log4j também usa o ThreadLocal para manter o MDC.
fonte
ThreadLocal
é útil quando você deseja ter algum estado que não deve ser compartilhado entre diferentes threads, mas deve ser acessível a partir de cada thread durante toda a sua vida útil.Como exemplo, imagine um aplicativo Web, em que cada solicitação é atendida por um encadeamento diferente. Imagine que, para cada solicitação, você precise de um pedaço de dados várias vezes, o que é bastante caro para calcular. No entanto, esses dados podem ter sido alterados para cada solicitação recebida, o que significa que você não pode usar um cache simples. Uma solução simples e rápida para esse problema seria ter uma
ThreadLocal
variável mantendo acesso a esses dados, para que você precise calculá-los apenas uma vez para cada solicitação. Obviamente, esse problema também pode ser resolvido sem o uso deThreadLocal
, mas eu o projetei para fins ilustrativos.Dito isto, tenha em mente que
ThreadLocal
s são essencialmente uma forma de estado global. Como resultado, ele tem muitas outras implicações e deve ser usado somente após considerar todas as outras soluções possíveis.fonte
ThreadLocal
como campo de objeto ...ThreadLocal
, onde era acessível globalmente. Como você disse, ainda pode ser usado como um campo de classe que limita a visibilidade.Nada realmente novo aqui, mas descobri hoje que
ThreadLocal
é muito útil ao usar a Validação de Bean em um aplicativo da web. As mensagens de validação são localizadas, mas por padrão são usadasLocale.getDefault()
. Você pode configurar oValidator
com um diferenteMessageInterpolator
, mas não há como especificar oLocale
quando você chamavalidate
. Portanto, você pode criar uma estáticaThreadLocal<Locale>
(ou, melhor ainda, um contêiner geral com outras coisas que talvez você precise serThreadLocal
e, em seguida,MessageInterpolator
escolher oLocale
que é personalizado. O próximo passo é escrever umServletFilter
que use um valor de sessão ourequest.getLocale()
escolher o local e armazenar no seuThreadLocal
referência.fonte
Como foi mencionado por @unknown (google), seu uso é definir uma variável global na qual o valor referenciado pode ser único em cada thread. Seus usos normalmente envolvem o armazenamento de algum tipo de informação contextual vinculada ao encadeamento atual de execução.
Nós o usamos em um ambiente Java EE para passar a identidade do usuário para classes que não têm conhecimento do Java EE (não têm acesso ao HttpSession ou ao EJB SessionContext). Dessa forma, o código, que faz uso da identidade para operações baseadas em segurança, pode acessar a identidade de qualquer lugar, sem ter que passar explicitamente em todas as chamadas de método.
O ciclo de operações de solicitação / resposta na maioria das chamadas Java EE facilita esse tipo de uso, pois fornece pontos de entrada e saída bem definidos para definir e desabilitar o ThreadLocal.
fonte
Por definição:
Cada um
Thread
em java contémThreadLocalMap
nele.Onde
Atingindo o ThreadLocal:
Agora crie uma classe de wrapper para o ThreadLocal que manterá o objeto mutável como abaixo (com ou sem
initialValue()
).Agora, o getter e o setter deste wrapper funcionarão na instância threadlocal em vez de no objeto mutável.
Se getter () de threadlocal não encontrou nenhum valor no threadlocalmap do
Thread
; em seguida, ele chamará o initialValue () para obter sua cópia particular em relação ao encadeamento.Agora
wrapper.getDateFormatter()
chamaráthreadlocal.get()
e isso verificará a instânciacurrentThread.threadLocalMap
contém esta (threadlocal).Se sim, retorne o valor (SimpleDateFormat) para a instância threadlocal correspondente, caso
contrário, adicione o mapa com essa instância threadlocal, initialValue ().
Com isso, a segurança do thread é alcançada nessa classe mutável; por cada thread está trabalhando com sua própria instância mutável, mas com a mesma instância ThreadLocal. Significa que todo o encadeamento compartilhará a mesma instância ThreadLocal da chave, mas diferentes instâncias SimpleDateFormat como valor.
https://github.com/skanagavelu/yt.tech/blob/master/src/ThreadLocalTest.java
fonte
As variáveis locais de encadeamento são frequentemente usadas para impedir o compartilhamento em projetos baseados em Singletons mutáveis ou variáveis globais.
Ele pode ser usado em cenários como fazer uma conexão JDBC separada para cada encadeamento quando você não estiver usando um Conjunto de Conexões.
Quando você chama getConnection, ele retornará uma conexão associada a esse thread. O mesmo pode ser feito com outras propriedades como dateformat, contexto de transação que você não deseja compartilhar entre threads.
Você também pode ter usado variáveis locais para o mesmo, mas esses recursos geralmente demoram na criação, portanto, você não deseja criá-las repetidamente sempre que executar alguma lógica de negócios com elas. No entanto, os valores ThreadLocal são armazenados no próprio objeto de encadeamento e, assim que o encadeamento é coletado de lixo, esses valores desaparecem também.
Este link explica muito bem o uso do ThreadLocal.
fonte
A classe ThreadLocal em Java permite criar variáveis que podem ser lidas e gravadas apenas pelo mesmo encadeamento. Portanto, mesmo que dois threads estejam executando o mesmo código e o código tenha uma referência a uma variável ThreadLocal, os dois threads não poderão ver as variáveis ThreadLocal um do outro.
Consulte Mais informação
fonte
Existem três cenários para usar um auxiliar de classe como SimpleDateFormat no código multithread, o melhor é usar ThreadLocal
Cenários
1- Usando o objeto de compartilhamento com a ajuda do mecanismo de bloqueio ou sincronização , que torna o aplicativo lento
2- Usando como objeto local dentro de um método
Nesse cenário, se tivermos 4 encadeamentos, cada um chamará um método 1000 vezes, teremos
4000 objetos SimpleDateFormat criados e aguardando o GC apagá-los
3- Usando ThreadLocal
se tivermos 4 threads e demos a cada thread uma instância SimpleDateFormat
, teremos 4 threads , 4 objetos de SimpleDateFormat.
Não há necessidade de mecanismo de bloqueio e criação e destruição de objetos. (Boa complexidade de tempo e complexidade de espaço)
fonte
[Para referência] O ThreadLocal não pode resolver problemas de atualização do objeto compartilhado. É recomendável usar um objeto staticThreadLocal que seja compartilhado por todas as operações no mesmo encadeamento. O método [Obrigatório] remove () deve ser implementado pelas variáveis ThreadLocal, especialmente ao usar conjuntos de encadeamentos nos quais os encadeamentos geralmente são reutilizados. Caso contrário, isso pode afetar a lógica comercial subsequente e causar problemas inesperados, como vazamento de memória.
fonte
Armazenamento em cache, em algum momento você deve calcular o mesmo valor por muito tempo, armazenando o último conjunto de entradas em um método e o resultado pode acelerar o código. Ao usar o Thread Local Storage, você evita pensar em travar.
fonte
ThreadLocal é uma funcionalidade especialmente provisionada pela JVM para fornecer um espaço de armazenamento isolado apenas para threads. como o valor da variável com escopo da instância, são vinculados a uma determinada instância apenas de uma classe. cada objeto tem seus únicos valores e eles não podem se ver. assim como o conceito de variáveis ThreadLocal, elas são locais para o segmento, no sentido de instâncias de objeto, outro segmento, exceto aquele que o criou, não pode vê-lo. Veja aqui
fonte
O Threadlocal fornece uma maneira muito fácil de obter a reutilização de objetos com custo zero.
Eu tive uma situação em que vários threads estavam criando uma imagem de cache mutável, em cada notificação de atualização.
Usei um Threadlocal em cada thread e, em seguida, cada thread precisaria redefinir a imagem antiga e atualizá-la novamente a partir do cache em cada notificação de atualização.
Objetos reutilizáveis comuns de conjuntos de objetos têm um custo de segurança de encadeamento associado a eles, enquanto essa abordagem não possui nenhum.
fonte
Experimente este pequeno exemplo, para ter uma idéia da variável ThreadLocal:
Saída do console, se o encadeamento BookA for feito primeiro:
Resultado BookA: 'wordA1, wordA2, wordA3'.
Livro de resultadosB: 'wordB1, wordB2'.
Saída do console, se o encadeamento BookB for feito primeiro:
Resultado BookB: 'wordB1, wordB2'.
Livro de resultadosA: 'wordA1, wordA2, wordA3'.
fonte