Desde Java 5, o pacote java.util.Concurrent.Locks contém algumas implementações de bloqueio, para que você não precise mais implementar seus próprios bloqueios. Mas você ainda precisa entender como usar esses bloqueios.
Uma trava simples
Vamos começar com um bloco de sincronização em Java:
public classe contador {private int conting = 0; public int inc () {sincronizado (this) {return ++ contagem; }}}Você pode ver que existe um bloco de código sincronizado (este) no método inc (). Este bloco de código pode garantir que apenas um thread possa executar a contagem de retorno ++ ao mesmo tempo. Embora o código no bloco de sincronização sincronizado possa ser mais complexo, a operação simples da contagem de ++ é suficiente para expressar o significado da sincronização do encadeamento.
A aula de contador a seguir usa o bloqueio em vez de sincronizado para atingir o mesmo objetivo:
Class de classe pública {private Lock = new Lock (); private int conting = 0; public int Inc () {Lock.lock (); int newCount = ++ contagem; Lock.Unlock (); retornar NewCount; }}O método Lock () bloqueia o objeto da instância de bloqueio; portanto, todos os threads que chamam o método Lock () no objeto serão bloqueados até que o método desbloqueio () do objeto de bloqueio seja chamado.
Aqui está uma implementação simples da classe de bloqueio:
classe pública contador {public class Lock {private boolean islocked = false; public sincronizado void Lock () lança interruptedException {while (islocked) {wait (); } islocked = true; } public sincronizado void desbloqueio () {islocked = false; notificar (); }}Observe o laço (islocked) dentro, que também é chamado de "bloqueio de spin". Quando Islocked é verdadeiro, o encadeamento chama bloqueio () e espera na chamada wait (). Para impedir que a rosca retorne de esperar () sem receber a chamada notify () (também chamada falsa despertar), o encadeamento verá novamente a condição islocked para determinar se pode ser continuado com segurança a executar ou precisa continuar esperando novamente, em vez de pensar que o fio pode ser continuado com segurança a executar após ser despertado. Se Islocked for falso, o encadeamento atual sairá do laço (islocked) e definirá de volta para o verdadeiro, para que outros threads chamando o método Lock () possam adicionar bloqueios na instância de bloqueio.
Quando o thread conclui o código na seção crítica (localizada entre Lock () e desbloqueio ()), o desbloqueio () é chamado. A execução do desbloqueio () redefinirá o Islocked para False e notificar (acordar) um dos threads que chamaram a função Wait () no método Lock () e estão em um estado de espera.
Reentabilidade de bloqueios
O bloco de sincronização sincronizado em Java é reentrante. Isso significa que, se um encadeamento Java entrar no bloco de sincronização sincronizado no código e, assim, obtiver uma trava no tubo correspondente ao objeto de sincronização usado pelo bloco de sincronização, o encadeamento pode inserir outro bloco de código Java sincronizado pelo mesmo objeto de tubulação. Aqui está um exemplo:
public class Reentrant {public sincronizado Outer () {Inner (); } public sincronizado interno () {// faça algo}}Observe que o Outer () e o Inner () são declarados sincronizados, o que é equivalente a blocos sincronizados (este) em Java. Se um thread chamar Outer (), não há problema em chamar interno () em Outer (), porque ambos os métodos (blocos de código) serão sincronizados pelo mesmo objeto de gerenciamento ("this"). Se um thread já tiver um bloqueio em um objeto de tubo, ele terá acesso a todos os blocos de código sincronizados pelo objeto Pipe. Isso é reentrante. Um encadeamento pode inserir qualquer bloco de código sincronizado por uma trava que já possui.
A implementação de bloqueio fornecida acima não é reentrante. Se reescrevermos a classe reentrante como a seguinte, quando o thread chama Outer (), ele bloqueará em Lock.lock () do método Inner ().
classe pública Reentrant2 {Lock Lock = new Lock (); public Outer () {Lock.lock (); interno(); Lock.Unlock (); } public sincronizado interno () {Lock.lock (); // faça algo Lock.unlock (); }}A chamada de thread Outer () bloqueará primeiro a instância de bloqueio e continuará ligando para o interior (). No método interno (), o thread tentará bloquear a instância de bloqueio novamente e a ação falhará (ou seja, o thread será bloqueado), porque a instância de bloqueio já está bloqueada no método Outer ().
Se o desbloqueio () não for chamado entre bloqueio () duas vezes, a segunda chamada para bloquear será bloqueada. Depois de ver a implementação de Lock (), você descobrirá que o motivo é óbvio:
classe pública Lock {boolean islocked = false; public sincronizado void Lock () lança interruptedException {while (islocked) {wait (); } islocked = true; } ...}Se um thread é permitido sair do método de bloqueio () é determinado pelas condições no loop while (bloqueio de spin). A condição atual de julgamento é que a operação de bloqueio é permitida apenas quando islocked é falsa, sem considerar qual encadeamento a trava.
Para tornar essa classe de bloqueio reentrável, precisamos fazer uma pequena mudança nela:
classe pública Lock {boolean islocked = false; Thread Lockedby = null; int bloqueioCount = 0; public sincronizado void Lock () lança interruptedException {thread CallingThread = Thread.currentThread (); while (islocked && bloqueado! = CallingThread) {wait (); } islocked = true; LockedCount ++; Lockedby = CallingThread; } public sincronizado void desbloqueio () {if (thread.currentThread () == this.lockedby) {LockedCount--; if (LockedCount == 0) {islocked = false; notificar (); }}} ...}Observe que a corrente enquanto o loop (bloqueio de spin) também leva em consideração o encadeamento que bloqueou a instância de bloqueio. Se o objeto de bloqueio atual não estiver bloqueado (islocked = false), ou o fio de chamada atual bloqueou a instância de bloqueio, então o loop while não será executado e o encadeamento chamando bloqueio () poderá sair do método (Nota do tradutor: "Permitido sair do método" na semântica atual significa que ele não chamará de espera () e Blockage).
Além disso, precisamos gravar o número de vezes que o mesmo encadeamento trava repetidamente um objeto de bloqueio. Caso contrário, uma chamada desblock () desbloqueará toda a fechadura, mesmo que o bloqueio atual tenha sido bloqueado muitas vezes. Não queremos que o bloqueio seja desbloqueado até que a chamada desbloqueio () atinja o número de vezes que a chamada de bloqueio correspondente () é chamada.
Agora esta classe de bloqueio é reentrante.
A justiça da fechadura
Os blocos sincronizados de Java não garantem a ordem em que os threads tentam inseri -los. Portanto, se vários threads continuarem a competir para acessar o mesmo bloco de sincronização sincronizado, existe o risco de que um ou mais threads nunca tenham acesso - ou seja, o acesso é sempre atribuído a outros threads. Esta situação é chamada de fome de thread. Para evitar esse problema, os bloqueios precisam alcançar a justiça. Os bloqueios mostrados neste artigo são implementados internamente com blocos de sincronização sincronizados, portanto não são garantidas como justas.
Ligue para o desbloqueio () na declaração finalmente
Se o bloqueio for usado para proteger a área crítica e a área crítica pode fazer uma exceção, é muito importante chamar o desbloqueio () na declaração finalmente. Isso garante que o objeto de bloqueio possa ser desbloqueado para que outros threads continuem a bloqueá -lo. Aqui está um exemplo:
Lock.lock (); tente {// faça código de seção crítico, // que pode lançar exceção} finalmente {Lock.unlock ();}Essa estrutura simples garante que o objeto de bloqueio possa ser desbloqueado quando uma exceção é lançada na área crítica. Se o desbloqueio () não for chamado na declaração finalmente, quando uma exceção for lançada na seção crítica, o objeto de bloqueio permanecerá no estado bloqueado para sempre, o que fará com que todos os outros threads que chamam de bloqueio () no objeto de bloqueio bloqueie.
O exposto acima são as informações sobre o bloqueio multi-thread Java. Continuaremos a adicionar informações relevantes no futuro. Obrigado pelo seu apoio a este site!