Lendo notas - combate de programação simultânea em Java - Capítulo 2 thread de segurança

A premissa para questões de segurança de thread:

Esta variável pode ser modificada

A variável é acessada por vários threads

Quando vários threads acessam a mesma variável de estado variável sem a sincronização adequada, ocorrerá um erro no programa. Existem três maneiras de resolver este problema:

  • Não compartilhe a variável de estado entre threads
  • Modifique variáveis ​​de estado para variáveis ​​imutáveis
  • Ao visitar, use a sincronização

1. O que é segurança de thread?

Segurança de thread: quando vários threads acessam uma determinada classe, a classe pode se comportar corretamente, então chamamos essa classe de thread-safe.

Objetos sem estado devem ser thread-safe

(Stateful e stateless: Stateful significa uma classe com a função de armazenar dados. Stateless significa que os atributos na classe não armazenam dados)

2. Atomicidade

1. Condição de corrida: Quando vários threads acessam o mesmo recurso, a ordem diferente de execução dos threads resultará em resultados inconsistentes.Neste momento, ocorre uma condição de corrida. Os recursos de código acessados ​​por vários threads são chamados de seções críticas.

  • Verifique primeiro e depois execute: faça um julgamento com base em uma observação que pode falhar, ou execute uma determinada operação. Esse tipo de condição de corrida é chamado de verificação antes da execução.

2. Condições de corrida na inicialização preguiçosa : a inicialização preguiçosa inicializará o objeto quando for necessário para economizar memória.Ao mesmo tempo, devemos garantir que o objeto seja inicializado apenas uma vez. No entanto, no caso de multithreading, duas threads executam a condição de inicialização ao mesmo tempo. Quando a inicialização da primeira thread não for concluída, a segunda thread executará o julgamento da condição neste momento. O resultado é que o objeto não foi inicializado, então a segunda thread executará a inicialização novamente. Problemas de segurança de thread ocorreram neste momento. O código que deveria ter sido executado uma vez foi executado uma vez por dois threads. Se houver outro thread para avaliar as condições de inicialização antes que a inicialização do primeiro e do segundo thread seja concluída. Então, mais uma inicialização será realizada. Esta é uma situação que não queremos ver.

3. Operação composta: a combinação de várias ações que devem ser executadas atomicamente é chamada de operação composta. Por exemplo, verifique primeiro e depois execute, há um conjunto de ações a serem executadas atomicamente. Portanto, verificar primeiro e depois executar é uma operação composta. Se quisermos garantir a segurança do thread, devemos garantir que cada grupo de operações compostas seja executado atomicamente. De modo geral, atingiremos esse objetivo por meio do bloqueio.

Três, mecanismo de bloqueio

1. Bloqueio embutido: Java fornece um mecanismo de bloqueio embutido para suportar atomicidade: blocos de código sincronizados. O bloco de código de sincronização consiste em duas partes: uma é a referência do objeto da fechadura e a outra é o bloco de código de proteção da fechadura. O método modificado com a palavra-chave Synchronized é um método de sincronização que inclui todo o corpo do método. O bloqueio deste bloco de código é uma instância de chamada deste método. É isso. Quando a modificação sincronizada é um método estático, o bloqueio do bloco de código é o objeto .CLASS da classe atual.

2. Reentrante: Quando um thread solicita um bloqueio mantido por outro thread, o thread solicitante será bloqueado. No entanto, como o bloqueio interno é reentrante, se um encadeamento tentar adquirir um bloqueio que já contém, a solicitação será bem-sucedida. Uma maneira de obter reentrada é associar o bloqueio a um contador e a um encadeamento proprietário. Quando o contador é 0, o bloqueio não é mantido por nenhum encadeamento. Neste momento, qualquer encadeamento que se aplica ao bloqueio pode ser bem-sucedido. Ao mesmo tempo, a JVM gravará o titular do bloqueio e definirá o contador para 1. Quando o mesmo encadeamento novamente Ao adquirir esse bloqueio, o contador aumentará e, quando a thread sair do bloco de código de sincronização, o contador diminuirá até que o contador alcance 0 e o bloqueio seja liberado.

3. Os benefícios da reentrada : Quando a subclasse substitui o método de sincronização da classe pai, conforme mostrado no código a seguir. Ao executar doSomething da subclasse LoggingWidget, a thread obterá o bloqueio. Quando o doSomething da subclasse é executado, o doSomething do pai é chamado novamente. Se o bloqueio interno não for reentrante, o doSomething da classe pai aguardará para sempre. A reentrada evita esse tipo de impasse.

public class Widget{
    
    
    public synchronized void doSomething(){
    
    
        ...
    }
}

public class LoggingWidget extends Widget{
    
    
    public synchronized void doSomething(){
    
    
        system.out.println("=================");
        super.doSomething();
    }
}

Quarto, use um cadeado para proteger o estado

As variáveis ​​podem ser protegidas por bloqueios, o que garante que apenas um thread esteja operando essa variável por vez. Somente quando o encadeamento conclui a operação na variável, outros encadeamentos podem operar na variável.

5. Atividade e desempenho

  • O bloqueio pode realmente garantir a segurança do nosso programa. Precisamos considerar a segurança porque: multi-threading é usado para melhorar o desempenho. No caso de multi-threading, problemas de segurança de thread ocorrerão, portanto, precisamos travar para garantir a sincronização. Mas isso não significa que devemos sacrificar o desempenho pela segurança. Portanto, devemos garantir a segurança do programa e, ao mesmo tempo, garantir o desempenho. Não sacrifique o desempenho pela segurança cega. Então, introduzimos o multithreading e isso não faz sentido.
  • Portanto, quando travamos, devemos fazer um julgamento razoável sobre o tamanho do bloco de código de sincronização. Neste ponto, precisamos encontrar uma compensação entre segurança, simplicidade e desempenho. Freqüentemente, há conflitos entre simplicidade e desempenho.

Nota: Ao realizar cálculos de longo prazo ou operações que podem não ser concluídas rapidamente (E / S de rede ou E / S de console), não segure o bloqueio

Acho que você gosta

Origin blog.csdn.net/weixin_45373852/article/details/108726264
Recomendado
Clasificación