Back-End

25 set, 2014

Aprenda mais sobre o Escopo Thread-Local

Publicidade

Dentro da programação de uma solução usando a tecnologia Java, os desenvolvedores tem a responsabilidade diária de escolher corretamente em qual escopo de ciclo de vida seus objetos existiram. Em minhas consultorias em geral, venho percebendo que a maioria dos profissionais Java desconhece completamente um dos escopos mais interessantes e muito eficiente, chamado de Thread-Local.

A falta de uso desse escopo em uma solução pode gerar complicadores na arquitetura, levando a implementação, na maioria das vezes, para o caminho da complexidade e inflexibilidade. Diante esse cenário, hoje eu gostaria de apresentar o conceito e prática desse escopo, fazendo com que programadores e projetistas Java possam se equipar com mais esse poderoso recurso do JSE.

O que é Thread-Local?

Thread-local é considerado mais um “escopo de acesso”, como outros muitos existentes dentro do Java. Ele é utilizado para definir o ciclo de vida dos objetos existentes durante a execução de um programa orientado a objetos. O grande diferencial desse escopo seja talvez o fato de ser completamente baseado em conceitos de programação concorrente, fazendo com ele fique um pouco mais complicado de se entender e usar.

Como funciona?

O escopo Thread-Local possui dois tipos de comportamento distintos que lhe diferencia drasticamente dos outros. Poderíamos chamá-los de global e local.

  • Global – todos os objetos armazenados em uma Thread-Local são globais para a thread corrente de execução. Isso que dizer que os métodos de quaisquer objetos sendo executados dentro de uma mesma thread terão visibilidade para este escopo.
  • Local – todos os objetos armazenados em uma Thread-Local estarão restritos somente para objetos sendo executados dentro da determinada thread. Isso que dizer que os métodos de quaisquer objetos sendo executados em outras threads não terão visibilidade para este escopo.

O ponto de referência para se assimilar a brincadeira é se conscientizar que quem executa um método de um objeto dentro de um programa Java é justamente algum objeto Thread (seja ele criado manualmente ou não). Dentro de cada thread existe então um lugar especial que pode ser usado para armazenar objetos pertencentes especificamente a thread. Todos as chamadas de objetos que forem empilhados naquela thread terão visibilidade transparente para esse escopo, podendo então usá-lo para os mais diferentes propósitos.

Para tentar clarear mais as ideias, eu poderia dizer que o escopo Thread-Local funciona basicamente da mesma forma que o famoso escopo estático. Todos os objetos colocados como estáticos no Java são criados apenas uma vez e ficam na memória até o termino da JVM. A única diferença é que os objetos colocados na Thread-Local ficam na memória até o termino da execução da determinada thread corrente. O escopo acaba quando a Thread terminar de ser executada.

A figura abaixo apresenta um gráfico que ilustra a escopo estático:

sem-tc3adtulo1

Temos na figura acima dois objetos do tipo thread invocando concorrentemente métodos de alguns objetos compartilhados e outros objetos não compartilhados. Tudo que é colocado como estático é globalmente compartilhado para todas as threads e todos os objetos sendo executados dentro de cada thread.

Já na próxima figura temos a representação que ilustra o escopo Thread-Local:

sem-tc3adtulo2

Temos na última figura dois objetos do tipo thread invocando concorrentemente os mesmo objetos da primeira figura. Nesse caso específico, o escopo Thread-Local age como um compartimento opcional colocado dentro da cada thread, na qual todos os métodos invocados dentro daquela determinada thread tem acesso transparente, podendo usá-lo para qualquer fim necessário.

Quando usar Thread-Local

Use em qualquer lugar que necessite compartilhar objetos em nível de execução de Thread. Alguns possíveis exemplos seriam: parâmetros de sistema, parâmetros de usuários, parâmetros de autenticação e autorização e até parâmetros de processos.

Um caso clássico de Thread-Local é a propagação de objetos de transação JDBC com chamadas recursivas. Objetos do padrão DAO poderiam usar este escopo para compartilhar a transação corrente, adicionando várias instruções SQL de diferentes objetos DAO. Frameworks que oferecem serviços transacionais para camadas de persistência como Spring utilizam-se dessa abordagem, podendo também ser facilmente implementada sem nenhum framework de terceiro.

A escopo Thread-Local é disponibilizado no Java usando a classejava.lang.ThreadLocal. Para todas as informações dessa classe, veja o JavaDoc.

Prática

Segue abaixo a implementação de um exemplo bem simples de Thread-Local.

31

A primeira classe chamada de ClasseThreadLocal foi utilizada para implementar o escopo Thread-Local. A segunda classe chamada de Regra foi utilizado para implementar o objeto que será executado concorrentemente dentro de várias threads. Veja que o método gravar() apenas acessa o valor dentro do escopo Thread-Local. Já a classe ProcessoThread foi utilizada para implementar as threads que serão executadas em paralelo. Veja que o método run() armazena um objeto do tipo String dentro do escopo Thread-Local, que será posteriormente acessível dentro do objeto implementado pela classe Regra. Por fim, vemos a classe Exemplo implementando o método main(), que faz toda a brincadeira acontecer. Veja que ele cria apenas um objeto Regra e quatro objeto ProcessoThread. Resumidamente, cada objeto thread executa concorrentemente o mesmo objeto Regra que transparentemente e separadamente acessa cada um valor diferente, pertencente a sua própria thread de execução.

Observação

Existe uma certa situação em usar Thread-Local dentro de containers JEE, uma vez que a maioria deles otimiza gastos com a criação de objetos threads usando abordagem de pool. Por isso, os objetos colocados dentro do escopo Thread-Local em execuções dentro de containers JEE podem ficar não elegíveis para o GC após a execução, uma vez a thread volta para o pool e fica disponível para a próxima invocação. A forma correta de tratar isso é limpar o escopo Thread-Local no final de cada execução, chamando o método ThreadLocal.remove().

Eu fico por aqui e espero que o artigo te ajude a usar esse incrível escopo.

Aquele abraço!