Cenários de aplicativos do ThreadLocal

No desenvolvimento comercial usual, o ThreadLocal possui dois cenários de uso típicos

Cenário 1:

O ThreadLocal é usado para salvar o objeto exclusivo de cada segmento, criando uma cópia para cada segmento, para que cada segmento possa modificar sua própria cópia sem afetar a cópia de outros segmentos, garantindo a segurança do segmento.

Cena 2:

ThreadLocal é usado como um cenário em que as informações precisam ser salvas independentemente em cada thread, para que outros métodos possam obter mais facilmente as informações. As informações obtidas por cada thread podem ser diferentes.Depois que o método executado anteriormente salva as informações, os métodos subsequentes podem ser obtidos diretamente através do ThreadLocal, que evita a passagem de parâmetros, semelhante ao conceito de variáveis ​​globais.

Cenário típico 1

Esse cenário geralmente é usado para salvar classes de ferramentas inseguras de threads.A classe típica que precisa ser usada é SimpleDateFormat .

Nesse caso, cada Thread tem sua própria cópia da instância e a cópia pode ser acessada e usada apenas pelo Thread atual, que é equivalente à variável local dentro de cada thread, que também é o significado da nomeação ThreadLocal. Como cada encadeamento possui sua própria cópia, e não uma cópia comum, não há problema em compartilhar entre vários encadeamentos.

Por exemplo, existem 10 threads que precisam usar SimpleDateFormat

public  class ThreadLocalDemo01 { 

    public  static  void main (String [] args) lança InterruptedException { 

        for ( int i = 0; i <10; i ++ ) {
             int finalI = i;
            new Thread (() -> { 
                String data = new ThreadLocalDemo01 (). date (finalI); 
                System.out.println (data); 
            }). start (); 
            Thread.sleep ( 100 ); 
        } 

    } 

    private String date ( int segundos) {
        Data data = nova data (1000 * segundos); 
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat ("mm: ss" );
        retornar simpleDateFormat.format (date); 
    } 
}

Criamos um objeto SimpleDateFormat para cada thread, eles não afetam um ao outro, o código pode ser executado normalmente. Resultado de saída:

00:00 
00:01 
00:02 
00:03 
00:04 
00:05 
00:06 
00:07 
00:08 
00:09

Vamos dar uma olhada neste estado atual com um gráfico:

 

 

E se o objeto SimpleDateFormat for usado por 1.000 threads?

Geralmente, não criamos diretamente tantos threads, mas através do pool de threads, como:

 classe pública ThreadLocalDemo011 {
    public  static ExecutorService threadPool = Executors.newFixedThreadPool (16 ); 

    public  static  void main (String [] args) lança InterruptedException { 

        for ( int i = 0; i <1000; i ++ ) {
             int finalI = i; 
            threadPool.submit (() -> { 
                String data = new ThreadLocalDemo011 (). date (finalI); 
                System.out.println (data); 
            }); 
        } 
        threadPool.shutdown ();
    }

     privadoData da string ( segundos int ) { 
        Data da data = nova Data (1000 * segundos); 
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat ("mm: ss" );
        retornar simpleDateFormat.format (date); 
    } 

}

Pode-se observar que usamos um pool de 16 threads e submetemos 1.000 tarefas a esse pool de threads. Faz a mesma coisa em cada tarefa que antes, ou executa o método date e cria um nesse método

objeto simpleDateFormat. O resultado:

00:00 
00:07 
00:04 
00:02 
... 
16:29 
16:28 
16:27 
16:26 
16:39

O que acabamos de fazer é criar um objeto simpleDateFormat para cada tarefa, ou seja, 1000 tarefas correspondem a 1000 objetos simpleDateFormat, mas e se o número de tarefas for grande?

A criação de tantos objetos é cara, e a destruição após o uso também é cara, e também é um desperdício de memória na memória.

Podemos pensar: todos os threads devem compartilhar um objeto simpleDateFormat? Mas simpleDateFormat não é seguro para threads, precisamos fazer a sincronização, como usar o sincronizado para bloquear. Aqui pode ser a nossa solução final. No entanto, o uso do bloqueio sincronizado cairá em um estado na fila e vários threads não poderão funcionar ao mesmo tempo, de modo que a eficiência geral seja bastante reduzida. Existe uma solução melhor?

Usar ThreadLocal

Para esse tipo de cenário, o ThreadLocal é mais apropriado.O ThreadLocal mantém um objeto simpleDateFormat para cada thread.Este objeto é independente entre os threads e não tem nada a ver um com o outro. Isso também evita problemas de segurança do encadeamento. Ao mesmo tempo, o objeto simpleDateFormat não criará muito, pois existem apenas 16 threads no pool de threads; portanto, são necessários 16 objetos.

 classe pública ThreadLocalDemo04 { 

    public  static ExecutorService threadPool = Executors.newFixedThreadPool (16 ); 

    public  static  void main (String [] args) lança InterruptedException { 

        for ( int i = 0; i <1000; i ++ ) {
             int finalI = i; 
            threadPool.submit (() -> { 
                String data = new ThreadLocalDemo04 (). date (finalI); 
                System.out.println (data); 
            }); 
        } 
        threadPool.shutdown (); 
    } 

    privadoData da string ( segundos int ) { 
        Data da data = nova Data (1000 * segundos); 
        SimpleDateFormat dateFormat = ThreadSafeFormater.dateFormatThreadLocal.get ();
        return dateFormat.format (date); 
    } 
} 

classe ThreadSafeFormater {
     public  estático ThreadLocal <SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial (() -> new SimpleDateFormat ("mm: ss" )); 
}

Vamos dar uma olhada neste estado atual com um gráfico:

 

 

Cenário típico 2

Cada encadeamento precisa salvar informações semelhantes às variáveis ​​globais (como informações do usuário obtidas no interceptador), que podem ser usadas diretamente por métodos diferentes para evitar problemas de passagem de parâmetros, mas não desejam ser compartilhadas por vários encadeamentos (porque os usuários obtêm por encadeamentos diferentes) A informação é diferente).

Por exemplo, use ThreadLocal para salvar algum conteúdo comercial (informações de permissão do usuário, nome de usuário obtido no sistema do usuário, ID do usuário etc.), as informações são as mesmas no mesmo encadeamento, mas diferentes encadeamentos usam conteúdo comercial diferente.

No ciclo de vida do encadeamento, todos os objetos que foram definidos pelo método estático get () da instância ThreadLocal são obtidos para evitar o problema de passar esse objeto (como o objeto de usuário) como parâmetro.

Por exemplo, se formos um sistema de usuário, quando uma solicitação chegar, um encadeamento será responsável por executar a solicitação e chamará service-1 (), service-2 (), service-3 (), serviço por sua vez. -4 (), esses 4 métodos podem ser distribuídos em diferentes classes.

 

Damos um exemplo na forma de figuras:

 

 

 

Código:

pacote público com.kong.threadlocal; 


public  class ThreadLocalDemo05 {
     public  static  void main (String [] args) { 
        Usuário user = new User ("jack" );
         new Service1 (). service1 (). service1 (user); 
    } 

} 

class Service1 {
     public  void service1 (Usuário usuário) {
         // Atribuir valor ao ThreadLocal, os serviços subseqüentes podem ser obtidos diretamente através do ThreadLocal. 
        UserContextHolder.holder.set (user);
         new Service2 (). Service2 (); 
    } 
} 

classe Service2 {
     public  voidservice2 () { 
        Usuário usuário = UserContextHolder.holder.get (); 
        System.out.println ( "Usuário que service2 recebeu:" + nome do usuário );
         novo Service3 (). service3 (). service3 (); 
    } 
} 

classe Service3 {
     public  void service3 () { 
        Usuário usuário = UserContextHolder.holder.get (); 
        System.out.println ( "Usuário que service3 recebeu:" + nome do usuário );
         // Após a execução de todo o processo, você deve executar remover 
        UserContextHolder .holder.remove (); 
    } 
} 

classe UserContextHolder {
     // Create ThreadLocal para salvar o objeto User
    público  estático ThreadLocal <User> holder = new ThreadLocal <> (); 
} 

classe Usuário { 
    Nome da string; 
    Usuário público (nome da string) {
         this .name = name; 
    } 
}

 

Os resultados da implementação:

Usuário Service2: jack 
Usuário Service3: jack

 

Acho que você gosta

Origin www.cnblogs.com/zz-ksw/p/12684877.html
Recomendado
Clasificación