Multithreading e notas de alta simultaneidade, vídeo de programação de GUI java

O thread atual dorme por milissegundos especificados

  • Thread.sleep([mills])

A thread atual cede graciosamente os direitos de execução

  • Thread.yield()

Por exemplo, Thread t1, t2, chame t1.join() no método run de t2, thread t2 aguardará que t1 seja concluído e executado

  • juntar

3. Estado da rosca

| Status | Cenários de uso |

| — | — |

| NOVO | Depois que o Thread é criado, antes de começar |

| RUNNABLE | Esta é a única maneira de um thread entrar no estado de execução após chamar o método start().

Ele é especificamente dividido em pronto e em execução.Quando o encadeamento é suspenso ou chama Thread.yield(), ele está pronto |

| WAITING | Quando um thread executa Object.wait(), ele deve estar esperando que outro thread execute Object.notify() ou Object.notifyAll().

Ou um thread thread, quando thread.join() é executado no thread principal, o thread principal aguardará a conclusão da execução do thread. Quando um thread executa LockSupport.park(), ele está esperando para executar LockSupport.unpark(thread). Quando o thread está nesse estado de espera, seu estado é WAITING. O que chama a atenção é que não há limite de tempo de espera aqui. Quando uma thread é encontrada neste estado, se ela estiver neste estado por muito tempo, também é necessário prestar atenção se há uma exceção de lógica no programa. |

|

TIMED_WAITING

| A diferença entre este estado e o estado WAITING é que a espera neste estado tem um certo limite de tempo

Thread.sleep(longo)

Object.wait(longo)

Tópico.join(longo)

LockSupport.parkNanos()

LockSupport.parkUntil()

|

| BLOQUEADO | O estado ao inserir um método ou bloco de código modificado pela palavra-chave sincronizada (adquirindo um bloqueio) |

| TERMINADO | O estado após o término da execução do encadeamento.

Depois que um thread termina, ele não pode ser ressuscitado.

Chamar o método start() em um encadeamento finalizado lançará uma java.lang.IllegalThreadStateException |

imagem

4. sincronizado

  • Os objetos estão bloqueados, não o código
  • isso é equivalente à classe atual.class
  • Método de bloqueio, método sem bloqueio ao mesmo tempo
  • Se ocorrer uma exceção durante a execução do bloqueio, o bloqueio será liberado automaticamente
  • O bloqueio obtido por sincronizado é reentrante
  • Bloqueio de atualização Bias Lock-Spin Lock-Heavyweight Lock
  • sincronizado (objeto) não pode usar constantes String/Integer, Long e outros tipos de dados básicos
  • Ao bloquear um objeto, certifique-se de que o objeto não pode ser reescrito e é melhor adicionar uma definição final

4. volátil

  • Visibilidade do thread garantida
  • Desativar a reordenação de instruções
  • Volátil não garante a consistência de várias modificações de encadeamento e a palavra-chave sincronizada ainda é necessária para manter a consistência
  • tipos de referência voláteis (incluindo arrays) só podem garantir a visibilidade da própria referência, não a visibilidade dos campos internos
volatile关 键字只能用于变量而不可以修饰方法以及代码块

5. Comparação de eficiência entre sincronizado e AtomicLong e LongAdder

O sincronizado precisa ser bloqueado e a eficiência é baixa;

AtomicLong não precisa solicitar um bloqueio e usa o mecanismo CAS;

LongAdder usa bloqueios segmentados, por isso é eficiente. Quando o número de simultaneidade é particularmente alto, LongAdder é mais adequado

6. Princípio de bloqueio de segmento de ConcurrentHashMap

O bloqueio de segmento serve para bloquear segmentos de dados e refinar ainda mais o bloqueio, o que ajuda a melhorar a eficiência da simultaneidade.

A razão pela qual o contêiner HashTable mostra ineficiência em um ambiente concorrente altamente competitivo é que todos os threads que acessam o HashTable devem competir pelo mesmo bloqueio. Se houver vários bloqueios no contêiner e cada bloqueio for usado para bloquear uma parte dos dados em o contêiner, então quando vários encadeamentos acessarem dados em diferentes segmentos de dados no contêiner, não haverá competição de bloqueio entre os encadeamentos, o que pode efetivamente melhorar a eficiência do acesso simultâneo. Esta é a tecnologia de segmentação de bloqueio usada pelo ConcurrentHashMap. Primeiro, os dados são divididos em seções e armazenados um a um e, em seguida, um bloqueio é atribuído a cada seção de dados. Quando um thread ocupa um bloqueio para acessar uma seção de dados, outras seções de dados também podem ser acessadas por outros threads .

7. Bloqueio Reentrante

ReentrantLock pode substituir sincronizado

No entanto, o ReentrantLock deve ser aberto/fechado manualmente. O sincronizado liberará automaticamente o bloqueio quando encontrar uma exceção. O ReentrantLock precisa ser fechado manualmente e geralmente é fechado no final

Defina o bloqueio Lock lock = new ReentrantLock();

abra lock.lock();

fechar lock.unlock();

Reentrantlock pode ser usado para "tentar bloquear" tryLock, de modo que não possa ser bloqueado ou dentro de um tempo especificado, e o thread pode decidir se continua esperando.

Use tryLock para tentar travar, independente de estar travado ou não, o método continuará sendo executado

Você pode determinar se deseja bloquear de acordo com o valor de retorno de tryLock

Você também pode especificar a hora de tryLock. Uma vez que tryLock(time) lança uma exceção, você deve prestar atenção ao processamento de unlock, que deve ser colocado finalmente. Se tryLock não estiver bloqueado, o desbloqueio não é necessário

O uso de ReentrantLock também pode chamar o método lockInterruptably, que pode responder ao método de interrupção do thread e pode ser interrompido enquanto um thread está aguardando o bloqueio.

new ReentrantLock(true) representa um bloqueio justo, sem parâmetros, o padrão é falso e um bloqueio injusto

8. CountdownLatch

A classe countDownLatch permite que um thread espere que outros threads concluam sua execução antes de executar.

É realizado por um contador, e o valor inicial do contador é o número de threads. Quando o método countDown() é chamado, sempre que um thread é executado, o valor do contador é -1. Quando o valor do contador é 0, significa que todos os threads concluíram a execução e, em seguida, o thread que aguarda o bloqueio pode retomar o trabalho. .

Chame o método countDown() no thread para iniciar a contagem;

Na thread que chama o método await(), a execução continuará quando o contador for 0, caso contrário, aguardará indefinidamente;

Você também pode usar latch.await(timeout, unit) para aguardar o tempo limite.Se o contador não for 0, o thread continuará.

O código após countDown() não é controlado pelo contador

Diferente de join, threads usando join serão bloqueadas, threads usando countDown não serão afetadas e só serão bloqueadas quando await for chamado

8. Barreira Cíclica

A função é fazer com que todos os threads do número especificado (número especificado pelo construtor) aguardem a conclusão antes de prosseguir para a próxima etapa.

Construtor:

public CyclicBarrier(int partes)

public CyclicBarrier(partes int, Ação de barreira executável)

partes é o número de threads;

A ação de barreira é a tarefa a ser executada pelo último thread que chega

Todos os encadeamentos aguardarão que todos os encadeamentos alcancem a cerca antes de continuar a execução, e o encadeamento que chegar por último concluirá a tarefa do Runnable.

Princípio de implementação: Um objeto Lock é definido dentro do CyclicBarrier. Sempre que um thread chama o método await, o número de threads interceptados é decrementado em 1, e então é julgado se o número restante de partes interceptadas é o valor inicial das partes. Se não for , insira a fila condicional da espera do objeto Lock. Em caso afirmativo, execute o método Runnable do objeto de barreiraAction e, em seguida, coloque todos os encadeamentos na fila de condição de bloqueio na fila de espera de bloqueio, e esses encadeamentos adquirirão e liberarão o bloqueio por sua vez.

9. Phaser

Uma barreira de sincronização reutilizável, semelhante em função ao CyclicBarrier e CountDownLatch, mas suporta um uso mais flexível.

O Phaser nos permite criar barreiras, etc., onde os segmentos lógicos precisam ir para a próxima etapa.

Podemos coordenar várias fases de execução, reutilizando instâncias do Phaser para cada fase do programa. Cada estágio pode ter um número diferente de threads esperando para avançar para outro estágio. Veremos um exemplo de uso de estágios posteriormente.

Para participar da coordenação, uma thread precisa se registrar() com uma instância de Phaser. Observe: isso apenas aumenta o número de partes registradas, não podemos verificar se o thread atual já está registrado - temos que criar uma subclasse da implementação para suportar isso.

Um thread o impede de atingir a barreira chamando arriAndAwaitAdvance(), que é um método de bloqueio. Quando o valor atingir o valor igual ao valor registrado, a execução do programa continuará e o valor aumentará. Podemos obter o valor atual chamando o método getPhase().

10. ReadWriteLock

A implementação específica de ReadWriteLock é ReentrantReadWriteLock

ReadWriteLock permite criar bloqueios de leitura e bloqueios de gravação separadamente

ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

Lock readLock = readWriteLock.readLock();

Lock writeLock = readWriteLock.writeLock();

Ao usar ReadWriteLock, a condição aplicável é que os mesmos dados sejam lidos por um grande número de threads, mas apenas alguns threads os modifiquem.

ReadWriteLock garante:

  • Apenas um thread tem permissão para escrever (outros threads não podem escrever nem ler);
  • Quando não está gravando, vários threads permitem leituras simultâneas (melhora o desempenho)

Os bloqueios de separação de leitura e gravação podem ajudar efetivamente a reduzir a concorrência de bloqueio para melhorar o desempenho do sistema. Os bloqueios de leitura e gravação não são mutuamente exclusivos entre leitura e gravação, e leitura e gravação e gravação e gravação são mutuamente exclusivos.

11. Semáforo

Um Semaphore é um semáforo de contagem que deve ser liberado pela thread que o adquiriu. Comumente usado para limitar o número de threads que podem acessar determinados recursos, como limitação por meio do Semaphore.

Para o Semaphore, o que ele precisa garantir é a exclusão mútua de recursos e não a sincronização de recursos.Ao mesmo tempo, a sincronização não pode ser garantida, mas a exclusão mútua de recursos pode ser garantida. Ele apenas limita o número de threads que acessam determinados recursos, mas na verdade não atinge a sincronização.

Métodos comuns:

1、adquire(int permite)

Adquire o número dado de permissões deste semáforo, bloqueando o thread até que essas permissões sejam concedidas ou o thread seja interrompido. É como um aluno ocupando duas janelas. Isso também corresponde ao método de liberação correspondente.

2、liberação(permissões int)

Libera o número de permissões fornecido, retornando-as ao semáforo. Isso corresponde ao método acima, quantas janelas devem ser liberadas após um aluno ocupar várias janelas

3、Permissões disponíveis()

Retorna o número de licenças atualmente disponíveis neste semáforo. Isso é para retornar quantas janelas estão disponíveis no momento.

4、reducePermits(redução int)

Diminui o número de licenças disponíveis pelo valor de redução especificado.

5、hasQueuedThreads()

Consulta se há threads esperando para adquirir recursos.

6, getQueueLength()

Retorna o número estimado de threads esperando para serem adquiridos. Este valor é apenas uma estimativa.

7、tryAcquire(int permite, tempo limite longo, unidade TimeUnit)

Adquire o número especificado de permissões deste semáforo se todas as permissões estiverem disponíveis para este semáforo dentro do tempo de espera especificado e o thread atual não for interrompido.

8、adquirir ininterruptamente (int permite)

Adquire o número especificado de permissões desse semáforo, bloqueando o thread até que essas permissões sejam fornecidas.

12. Trocador

Uma classe de ferramenta de encapsulamento para troca de dados entre dois threads de trabalho. Simplificando, um thread deseja trocar dados com outro thread após concluir uma determinada transação, e o primeiro thread que obtém os dados primeiro aguardará o segundo. Threads não podem trocar dados correspondentes entre si até que o segundo thread chegue com os dados. É definido como um tipo genérico do Exchanger, onde V representa um tipo de dado trocável, e a interface fornecida para o mundo exterior é muito simples, conforme a seguir:

Exchanger(): Nenhum método de construção de parâmetro.

V exchange(V v): Espera que outro thread alcance este ponto de troca (a menos que o thread atual seja interrompido), então transfere o objeto fornecido para este thread e recebe o objeto deste thread.

V exchange(V v, timeout longo, unidade TimeUnit): espera que outro thread alcance este ponto de troca (a menos que o thread atual seja interrompido ou exceda o tempo de espera especificado), então transfere o objeto fornecido para este thread e recebe o objeto de fio.

13. LockSupport

LockSupport é uma ferramenta de bloqueio de rosca muito conveniente e prática, que pode bloquear roscas em qualquer posição.

O método estático park() do LockSupport pode bloquear o thread atual, da mesma forma que existem parkNanos(), parkUntil(), etc., eles implementam uma espera limitada por tempo.

| Método | Descrição |

| — | — |

| void park(): | Bloqueia o thread atual, se o método unpark for chamado ou o thread atual for interrompido, ele pode retornar do método park() |

| void park(Object blocker) | A função é a mesma do método 1, e um objeto Object é adicionado como um parâmetro para registrar o objeto de bloqueio que causa o bloqueio do encadeamento, para facilitar a solução de problemas; |

| void parkNanos(long nanos) | Bloquear o thread atual, o mais longo não é mais do que nanos nanossegundos, adicionando o recurso de retorno de tempo limite; |

| void parkNanos(Object blocker, long nanos) | A função é a mesma do método 3, e um objeto Object é adicionado como um parâmetro para registrar o objeto de bloqueio que causa o bloqueio do encadeamento, para facilitar a solução de problemas; |

| void parkUntil(long deadline) | bloqueia o thread atual até o deadline; |

| void parkUntil(Object blocker, long deadline) | A função é a mesma do método 5, e um objeto Object é adicionado como parâmetro para registrar o objeto de bloqueio que causa o bloqueio do thread, para facilitar a solução de problemas; |

Da mesma forma, existe um método de bloqueio, é claro que existe um método de ativação, o quê? método unpark(Thread). Este método pode ativar o thread especificado.

"Análise das perguntas da entrevista Java em fabricantes de primeira linha + notas de estudo de desenvolvimento de back-end + vídeo de explicação de arquitetura mais recente + folhetos reais do código-fonte do projeto"

[docs.qq.com/doc/DSmxTbFJ1cmN1R2dB] Compartilhamento de código aberto de conteúdo completo

Deve-se notar que a ordem de execução do método park e do método unpark não é tão rígida. Por exemplo, se o método suspend e o método resume que mencionamos na classe Thread, se a ordem estiver errada, ele nunca será ativado, mas o método park e o método unpark não, porque o LockSupport usa um mecanismo semelhante aos semáforos. Ele prepara uma licença para cada thread (não disponível por padrão). Se a licença estiver disponível, a função park retornará imediatamente e consumirá a licença (ou seja, tornará a licença indisponível). Se a licença não estiver disponível, bloqueará . O método unpark disponibiliza uma licença

14. AQS

AQS é a abreviação de AbstractQueuedSynchronizer

AQS é uma estrutura de sincronização fornecida no JDK para implementar bloqueios de bloqueio e sincronizadores relacionados com base em filas de espera FIFO.

Essa classe abstrata é projetada como uma classe base para alguns sincronizadores cujo estado pode ser representado por um valor int atômico.

O AQS gerencia um único inteiro de informações de estado, que pode representar qualquer estado.

#por exemplo

O semáforo o usa para representar o número restante de licenças,

ReentrantLock o usa para mostrar quantas vezes o thread que o possui solicitou um bloqueio;

FutureTask usa para representar o estado da tarefa (ainda não iniciada, em execução, concluída e cancelada)

  • Termos e Condições

Uso

Para usar esta classe como base de um sincronizador, redefina os seguintes métodos, conforme aplicável, inspecionando e/ou modificando o estado de sincronização usando {@link #getState}, {@link #setState} e/ou {@link #compareAndSetState }:

  • {@link #tryAcquire}

  • {@link #tryRelease}

  • {@link #tryAcquireShared}

  • {@link #tryReleaseShared}>

  • {@link #isHeldExclusively}

Os métodos acima não precisam ser totalmente implementados, e diferentes métodos podem ser implementados de acordo com o tipo de bloqueio adquirido:

Os sincronizadores que suportam aquisição exclusiva (exclusiva) de bloqueios devem implementar tryAcquire, tryRelease, isHeldExclusively;

Um sincronizador que suporta bloqueios de aquisição compartilhada deve implementar tryAcquireShared, tryReleaseShared e isHeldExclusively.

  • Análise de AQS

A realização do AQS é principalmente para manter um "estado int volátil" (representando recursos compartilhados) e

Uma fila de espera de encadeamento FIFO (quando vários encadeamentos competem por recursos e são bloqueados, eles entrarão nessa fila).

Cada nó na fila é um encapsulamento do thread, incluindo as informações básicas do thread, o estado, o tipo de recurso a ser aguardado e assim por diante.

Existem três maneiras de acessar #state:

getState()

setState()

compareAndSetState()

#AQS define dois métodos de compartilhamento de recursos

Exclusivo (exclusivo, apenas um thread pode executar, como ReentrantLock)

Compartilhar (compartilhado, vários threads podem ser executados ao mesmo tempo, como Semaphore/CountDownLatch)

Diferentes sincronizadores personalizados competem por recursos compartilhados de maneiras diferentes.

Quando o sincronizador personalizado é implementado, ele precisa apenas realizar a aquisição e liberar o estado do recurso compartilhado.

Quanto à manutenção da fila de espera de thread específica (como enfileirar/acordar da fila após falha na obtenção de recursos, etc.), o AQS foi implementado no nível superior.

Ao implementar um sincronizador personalizado, os seguintes métodos são implementados principalmente:

isHeldExclusively(): Se o thread está segurando recursos exclusivamente. Só é necessário implementá-lo quando a condição for usada.

tryAcquire(int): Modo exclusivo. Tenta buscar o recurso, retornando true em caso de sucesso e false em caso de falha.

tryRelease(int): Modo exclusivo. Tenta liberar o recurso, retornando true em caso de sucesso e false em caso de falha.

tryAcquireShared(int): método de compartilhamento. Tente buscar recursos. Um número negativo indica falha; 0 indica sucesso, mas nenhum recurso foi deixado; um número positivo indica sucesso, com recursos restantes.

tryReleaseShared(int): método de compartilhamento. Tenta liberar o recurso, se for permitido ativar o nó de espera subseqüente após a liberação, retorna verdadeiro, caso contrário, retorna falso.

Tome ReentrantLock como exemplo

O estado é inicializado em 0, indicando o estado desbloqueado.

Quando um thread lock (), ele chamará tryAcquire () para monopolizar o bloqueio e o estado +1.

Depois disso, outros encadeamentos falharão quando tryAcquire(), até que A thread unlock() to state=0 (ou seja, libere o bloqueio), outros encadeamentos terão a oportunidade de adquirir o bloqueio.

Obviamente, antes que o bloqueio seja liberado, o encadeamento A pode adquirir o bloqueio repetidamente (o estado se acumulará), que é o conceito de reentrância.

Mas preste atenção em quantas vezes você precisa liberá-lo, para garantir que o estado possa voltar a zero.

Tome CountDownLatch como exemplo

A tarefa é dividida em N sub-threads para executar e o estado também é inicializado para N (observe que N deve ser consistente com o número de threads).

Esses N sub-threads são executados em paralelo. Após cada sub-thread ser executado, countDown() uma vez, e o estado será decrementado pelo CAS em 1.

Depois que todos os sub-threads forem executados (ou seja, state=0), o thread de chamada principal será unpark() e, em seguida, o thread de chamada principal retornará da função await() para continuar as ações restantes.

Em geral, sincronizadores personalizados são métodos exclusivos ou métodos compartilhados,

Eles também precisam implementar apenas um dos tryAcquire-tryRelease, tryAcquireShared-tryReleaseShared.

Mas o AQS também oferece suporte a sincronizadores personalizados para implementar métodos exclusivos e compartilhados, como "ReentrantReadWriteLock".

15. Conceito básico de fechadura

  • bloqueio justo/bloqueio injusto
  • fechadura reentrante

Acho que você gosta

Origin blog.csdn.net/m0_65484000/article/details/122133561
Recomendado
Clasificación