Galeria de mapas mentais Ferramenta de bloqueio de simultaneidade Java
Este é um mapa mental sobre ferramentas de bloqueio de simultaneidade Java. Essas ferramentas de bloqueio de simultaneidade podem desempenhar um papel importante na programação simultânea de Java e ajudá-lo a escrever código simultâneo eficiente e seguro.
Editado em 2024-01-18 10:28:15이것은 (III) 저산소증-유도 인자 프롤릴 하이드 록 실라 제 억제제에 대한 마인드 맵이며, 주요 함량은 다음을 포함한다 : 저산소증-유도 인자 프롤릴 하이드 록 실라 제 억제제 (HIF-PHI)는 신장 빈혈의 치료를위한 새로운 소형 분자 경구 약물이다. 1. HIF-PHI 복용량 선택 및 조정. Rosalasstat의 초기 용량, 2. HIF-PHI 사용 중 모니터링, 3. 부작용 및 예방 조치.
이것은 Kuka Industrial Robots의 개발 및 Kuka Industrial Robot의 모션 제어 지침에 대한 마인드 맵입니다. 주요 내용에는 쿠카 산업 로봇의 역사, 쿠카 산업 로봇의 특성, 쿠카 산업 로봇의 응용 분야, 2. 포장 프로세스에서 쿠카 로봇은 빠르고 일관된 포장 작업을 달성하고 포장 효율성을 높이며 인건비를 줄입니다. 2. 인건비 감소 : 자동화는 운영자에 대한 의존성을 줄입니다. 3. 조립 품질 향상 : 정확한 제어는 인간 오류를 줄입니다.
408 컴퓨터 네트워크가 너무 어렵습니까? 두려워하지 마세요! 나는 피를 구토하고 지식 맥락을 명확히하는 데 도움이되는 매우 실용적인 마인드 맵을 분류했습니다. 컨텐츠는 매우 완전합니다. 네트워크 아키텍처에서 응용 프로그램 계층, TCP/IP 프로토콜, 서브넷 디비전 및 기타 핵심 포인트에 이르기까지 원칙을 철저히 이해하는 데 도움이 될 수 있습니다. 📈 명확한 논리 : Mindmas 보물, 당신은 드문 기회가 있습니다. 서둘러! 이 마인드 맵을 사용하여 408 컴퓨터 네트워크의 학습 경로에서 바람과 파도를 타고 성공적으로 해변을 얻으십시오! 도움이 필요한 친구들과 공유해야합니다!
이것은 (III) 저산소증-유도 인자 프롤릴 하이드 록 실라 제 억제제에 대한 마인드 맵이며, 주요 함량은 다음을 포함한다 : 저산소증-유도 인자 프롤릴 하이드 록 실라 제 억제제 (HIF-PHI)는 신장 빈혈의 치료를위한 새로운 소형 분자 경구 약물이다. 1. HIF-PHI 복용량 선택 및 조정. Rosalasstat의 초기 용량, 2. HIF-PHI 사용 중 모니터링, 3. 부작용 및 예방 조치.
이것은 Kuka Industrial Robots의 개발 및 Kuka Industrial Robot의 모션 제어 지침에 대한 마인드 맵입니다. 주요 내용에는 쿠카 산업 로봇의 역사, 쿠카 산업 로봇의 특성, 쿠카 산업 로봇의 응용 분야, 2. 포장 프로세스에서 쿠카 로봇은 빠르고 일관된 포장 작업을 달성하고 포장 효율성을 높이며 인건비를 줄입니다. 2. 인건비 감소 : 자동화는 운영자에 대한 의존성을 줄입니다. 3. 조립 품질 향상 : 정확한 제어는 인간 오류를 줄입니다.
408 컴퓨터 네트워크가 너무 어렵습니까? 두려워하지 마세요! 나는 피를 구토하고 지식 맥락을 명확히하는 데 도움이되는 매우 실용적인 마인드 맵을 분류했습니다. 컨텐츠는 매우 완전합니다. 네트워크 아키텍처에서 응용 프로그램 계층, TCP/IP 프로토콜, 서브넷 디비전 및 기타 핵심 포인트에 이르기까지 원칙을 철저히 이해하는 데 도움이 될 수 있습니다. 📈 명확한 논리 : Mindmas 보물, 당신은 드문 기회가 있습니다. 서둘러! 이 마인드 맵을 사용하여 408 컴퓨터 네트워크의 학습 경로에서 바람과 파도를 타고 성공적으로 해변을 얻으십시오! 도움이 필요한 친구들과 공유해야합니다!
Ferramenta de bloqueio de simultaneidade Java
ResumoQueueSynchronizer
Introdução
A estrutura básica para construir bloqueios ou outros componentes de sincronização, usados principalmente para gerenciar o status de sincronização
A principal forma de usá-lo é herança
O bloqueio só pode ocorrer em um momento, reduzindo o custo da troca de contexto.
herdar
ResumoOwnableSynchronizer
Implementação básica do sincronizador que permite que threads ocupem o modo exclusivo
Variáveis de membro
Tópico transitório privado exclusivoOwnerThread
O thread atualmente ocupando o estado de sincronização
método principal
set final nulo protegidoExclusiveOwnerThread (thread thread)
Definir tópico exclusivo
Thread final protegido getExclusiveOwnerThread()
Obtenha o tópico exclusivo atual
implementar interface
Serializável
classe interna
Nó de classe final estática
Introdução
O AQS usa internamente uma fila de sincronização FIFO bidirecional para concluir o gerenciamento do status de sincronização. Quando um thread não é obtido, ele é adicionado ao final da fila. A estrutura da fila é Nó; na AQS. A fila de condição também usa a definição do Node, mas está em outra fila
Variáveis de membro
constante estática
Nó final estático SHARED = novo Nó()
Indica que o nó está em modo compartilhado
Nó final estático EXCLUSIVE = null
Indica que o nó está em modo exclusivo e apenas um thread mantém o estado de sincronização ao mesmo tempo.
int final estático CANCELADO = 1
O nó (thread) está neste estado devido a timeout ou interrupção, e não mudará para outros estados posteriormente, nem deverá mais ser bloqueado.
final estático int SINAL = -1
Quando o nó (thread) está neste estado, o nó sucessor está ou estará em um estado bloqueado (através do parque), portanto, quando o nó atual libera o bloqueio ou é cancelado, unpark precisa ser chamado para ativar o nó sucessor .
final estático int CONDIÇÃO = -2
Indica que o nó atual (thread) está na fila de condições (via await) e não faz parte da fila de sincronização até que o status seja definido como 0 em um determinado momento (via sinal)
final estático int PROPAGATE = -3
Propagar bloqueios compartilhados (usados pelo nó principal)
volátil int waitStatus
Incluindo CANCELLED/SIGNAL/CONDITION/PROPAGATE acima e 0 (indicando que não está nesses quatro estados), um número não negativo indica que o nó não requer interação
Nó volátil anterior
Nó precursor
Nó volátil próximo
nó sucessor
thread volátil
O thread correspondente ao nó
Nó nextWaiter
Na fila de sincronização indica se o nó está em estado compartilhado ou exclusivo, igual a SHARED para indicar compartilhamento e nulo para indicar exclusivo na fila condicional indica uma referência ao próximo nó;
A condição só pode ser usada quando a fila de sincronização está em modo exclusivo, portanto, esta variável foi projetada para ser compartilhada.
método principal
Método de construção
Nó()
Usado para estabelecer nós iniciais ou construir nós SHARED
Nó (thread thread, modo nó)
Para uso em addWaiter
Nó (thread thread, int waitStatus)
Usado em condições
método membro
booleano final isShared()
return nextWaiter == COMPARTILHADO
antecessor do nó final()
Obtenha o nó predecessor, lance uma exceção quando for nulo
classe pública ConditionObject
Introdução
A implementação da interface Condition serve à implementação básica do Lock. Use o Node para construir uma fila de espera. Cada objeto Condition contém uma fila FIFO (unidirecional).
A fila usa a propriedade nextWaiter do Node como referência para o próximo nó.
implementar interface
Doença
Serializável
Variáveis de membro
constante estática
privado estático final int REINTERRUPT = 1
Indica a necessidade de interromper novamente ao sair da espera
privado estático final int THROW_IE = -1
Indica que InterruptedException precisa ser lançada ao sair da espera
Nó transitório privado firstWaiter
Aponta para o nó principal da fila condicional
Nó transitório privado lastWaiter
Aponta para o nó final da fila de condições
método principal
público final vazio aguarda ()
Faça o thread de chamada entrar na fila de espera, liberar o bloqueio e entrar no estado de bloqueio. Após o nó ser despertado por signal/signalAll, tente adquirir o bloqueio. Se o bloqueio for despertado por uma interrupção, ele responderá à interrupção.
Nó privado addConditionWaiter()
Adicione um novo nó à fila de espera.
Lógica de implementação específica: quando for descoberto que lastWaiter foi cancelado, chame unlinkCancelledWaiters para limpar os métodos cancelados em toda a fila (tanto firstWaiter quanto lastWaiter podem apontar para novos nós, em seguida, criar um novo nó (estado CONDITION) e adicioná-lo ao nó); fim da fila
private void unlinkCancelledWaiters()
Use while para limpar nós cujo waitStatus não seja CONDITION de firstWaiter em diante. Como a chamada do método ocorre antes da liberação do bloqueio, o processo de loop não precisa ser bloqueado. Para evitar resíduos no GC, quando não há sinal, este método só é utilizado quando ocorre timeout ou cancelamento.
O waitStatus do nó na fila de condições deve ter apenas dois estados: CONDITION e CANCELLED.
final int totalmenteRelease (nó nó)
Liberar o estado de bloqueio. Se o estado de bloqueio não for liberado, uma IllegalMonitorStateException será lançada e o nó será definido para o estado CANCELED. Se for bem-sucedido, o valor do estado antes da liberação será retornado.
liberar
O método de liberação do AQS é chamado aqui e um nó adequado é encontrado para despertar.
Os parâmetros usam o atributo state do AQS
final booleano isOnSyncQueue (nó nó)
Determine se o nó está na fila de sincronização (observe que esta é uma fila de sincronização em vez de uma fila condicional): Quando o waitStatus é CONDIÇÃO, significa que ele não está na fila de sincronização se o pré ou próximo do nó estiver. nulo, também significa que não está na fila de sincronização (esses dois atributos são usados para a fila de sincronização. O antecessor e o sucessor da fila condicional não usam esses dois atributos) e, em seguida, chame o método findNodeFromTail para percorrer a sincronização fila do nó final para ver se o nó está nele.
A relação entre a fila de sincronização e a fila de condição: a competição de bloqueio depende apenas da fila de sincronização. A fila de condição salva apenas threads bloqueados por falta de condições. Quando os threads precisam competir por bloqueios, eles ainda precisam ser convertidos em fila de sincronização.
Este método é usado como condição de julgamento do while no código. Se o nó não estiver na fila de sincronização (indicando que não é signal/signalAll), ele entrará no while e bloqueará o park. Quando o nó for despertado, ele primeiro determinará se foi despertado por uma interrupção. Caso contrário, ele continuará retornando ao processo while. processo.
adquirirQueued(nó, estado salvo)
Constantemente tentando obter o status de sincronização do saveState para os nós na fila de sincronização
desvincularCancelledWaiters
Após adquirir o bloqueio, se node.nextWaiter não for nulo, chame este método para limpar o nó no estado cancelado.
reportInterruptAfterWait
Se foi despertada devido a uma interrupção anterior, a interrupção precisa ser processada com base nos resultados do julgamento anterior, seja para redefinir o status da interrupção ou lançar uma exceção de interrupção.
final público muito aguardaNanos(long nanosTimeout)
A lógica básica é semelhante à espera, com a adição do julgamento de tempo limite.
Não entendo muito bem a última parte de transferAfterCancelledWait.
public final boolean wait(muito tempo, unidade TimeUnit)
A lógica básica é semelhante a awaitNanos, você pode especificar a unidade de tempo e, por fim, ela é convertida em nanossegundos para cálculo.
public final void aguarda ininterruptamente()
A lógica básica é semelhante a aguardar e não responde a interrupções (ou seja, selfInterrupt redefine o status da interrupção)
público final booleano awaitUntil (data prazo)
A lógica básica é semelhante à espera, com a adição de um tempo máximo de bloqueio.
sinal de vazio final público()
Mova o thread de espera mais longo da fila condicional para a fila de sincronização
booleano protegido isHeldExclusively()
Retorna se o thread atual mantém o bloqueio em modo exclusivo; em caso afirmativo, retorna verdadeiro. Este método está em AQS e não fornece uma implementação específica. Porém, como este método é usado apenas em condições, ele não precisa ser implementado se ConditionObject não for usado, caso contrário, ele precisa ser implementado.
private void doSignal(nó primeiro)
Remova os nós da fila de condições e converta-os em nós da fila de sincronização. O processo de implementação é um loop, de frente para trás, até encontrar um nó que não seja nulo e não esteja no estado CANCELADO, converta-o e saia do loop
transferência booleana finalForSignal (nó nó)
Converta o nó da fila condicional para a fila de sincronização e retorne se a conversão foi bem-sucedida. A lógica de implementação é primeiro determinar se o status do nó é CONDIÇÃO. Se não for um nó cancelado, retorne falso e chame enq para entrar na fila de sincronização e obter o nó predecessor do nó na fila. o status do nó predecessor é > 0 ou CAS foi modificado Se waitStatus falhar (ocorreram alterações), desestacione o encadeamento e finalmente retorne verdadeiro
Ele não é necessariamente ativado diretamente após ingressar na fila de sincronização. Somente quando waitStatus>0 ou alterações durante a execução do CAS o encadeamento será desestacionado e despertado. Se você não acordar neste momento, você esperará até a lógica da fila de sincronização e então acordará.
eq
sinal de vazio final públicoAll()
Transfira todos os nós qualificados (não cancelados) da fila de condições para a fila de sincronização. A lógica básica é semelhante ao signal, a diferença é que o método transferForSignal é executado em um loop
é mantido exclusivamente
private void doSignalAll(nó primeiro)
transferirForSignal
hasWaiters booleano final protegido()
Se houver threads na fila de condições, o método de implementação é percorrer a fila de condições desde o início. Se houver um nó cujo status seja CONDIÇÃO, ele retornará verdadeiro, caso contrário, retornará falso.
Variáveis de membro
variável estática
spinForTimeoutThreshold longo final estático = 1000L;
O limite usado para timeout se for menor que esse valor, não há necessidade de chamar park com timeout, mas deixar o loop continuar a execução. Neste momento, a eficiência do spin é maior.
privado estático final Inseguro inseguro
Unsafe.getUnsafe(), os itens a seguir são usados para obter o deslocamento por meio do método objectFieldOffset, para que modificações subsequentes possam ser feitas diretamente por meio da operação CAS de Unsafe.
estado final longo estático privadoOffset
Estado AQS
headOffset final longo estático privado
Chefe AQS
private static final long tailOffset
Cauda AQS
private static final longa esperaStatusOffset
WaitStatus do nó
private static final longo nextOffset
Nó próximo
Cabeça de nó volátil transitória privada
O nó principal da fila de sincronização, carregamento lento, modificado apenas por meio de setHead quando o nó principal existe, seu status não pode ser CANCELADO;
cauda do nó volátil transitório privado
O nó final da fila de sincronização, carregamento lento
estado interno volátil privado
Status de sincronização
método principal
final protegido int getState()
Retorna o valor atual do status de sincronização
setState final vazio protegido (int newState)
Definir o status de sincronização atual
booleano final protegido compareAndSetState(int expect, int update)
CAS define o estado e a camada inferior chama o método CAS de Unsafe.
Nó privado addWaiter (modo Nó)
método de enfileiramento
Adicione o thread atual ao final da fila usando o modo especificado
Vale a pena notar que
O parâmetro Node é usado para especificar o modo, e o thread é obtido por meio de currentThread
Na implementação, quando a cauda não estiver vazia, tente cas para definir a cauda. Se for bem-sucedido, ele retornará diretamente. Se falhar, o método enq será chamado.
enq do nó privado (nó final)
Loop (spin) CAS. Quando tail está vazio, CAS inicializa head (novo nó) primeiro. Quando tail não está vazio, CAS define tail.
O nó de parâmetro aqui é o nó que precisa ser adicionado em vez do modo.
público final booleano hasQueuedPredecessors()
Determine se existe um nó antes do thread atual; em caso afirmativo, retorne verdadeiro, caso contrário, retorne falso. Usado para a implementação de fair locks. Implementação específica: quando a fila não está vazia, ela retorna verdadeiro se for considerado que o nó sucessor do cabeçalho está vazio ou não é o thread atual.
Quando head.next é nulo?
público final booleano hasQueuedThreads()
return head != tail Se há threads esperando na fila
público final booleano isQueued(Thread thread)
Esteja o thread na fila, a implementação deve fazer um loop do final ao início
público final int getQueueLength()
Obtenha o comprimento da fila. O método de implementação consiste em fazer um loop do final ao início para calcular o número de nós cujo thread não é nulo.
Coleção final pública<Thread> getQueuedThreads()
Obtém a coleção de threads na fila e a retorna na forma de Collection. A implementação consiste em construir um ArrayList, fazer um loop do final ao início para adicionar threads que não são nulos e, em seguida, retornar o objeto ArrayList.
propriedade booleana final pública (condição ConditionObject)
Se o objeto de condição pertence ao sincronizador atual AQS
modo exclusivo
Apenas um thread pode obter o status de sincronização ao mesmo tempo
aquisição pública final nula (int arg)
O modo exclusivo obtém o status de sincronização e ignora as interrupções; primeiro chame tryAcquire para tentar adquirir o bloqueio exclusivo e, após falha, chame adquireQueued(addWaiter(Node.EXCLUSIVE), arg) para tentar adicionar o thread à fila de sincronização e determinar o status do nó e, em seguida, com base no resultado retornado (se houver (interrupção de thread) Chame o método de interrupção de thread para restaurar o estado de interrupção
a aquisição em si não responde às interrupções, portanto, o processamento das interrupções é para restaurar o status da interrupção
booleano protegido tryAcquire(int arg)
Tente obter um bloqueio exclusivo, retorne verdadeiro se for bem-sucedido e falso se falhar. A implementação específica é concluída por subclasses e a segurança do thread precisa ser garantida ao obter o status de sincronização.
aquisição booleana finalQueued (nó final, int arg)
arg pode ser entendido como um recurso que precisa ser competido. O AQS é representado internamente pelo estado, mas na verdade o uso específico é definido pelo desenvolvedor.
Tenta continuamente adquirir bloqueios para threads já na fila (o nó foi criado por addWaiter)
Lógica de implementação: Gire da seguinte forma: Se o nó predecessor for head e adquirir o bloqueio com sucesso, defina o nó atual como head e o retorno não será interrompido, caso contrário, prossiga para as seguintes operações: chame shouldParkAfterFailedAcquire para determinar e definir o status do nó e; chame parkAndCheckInterrupt para bloquear o thread e verificar o status da interrupção. Se ocorrer uma exceção durante o processo, chame cancelAcquire para cancelar a aquisição.
Na verdade, mesmo que o bloqueio não seja adquirido, o loop não irá parar. Quando o status do nó predecessor for definido com sucesso e o thread for interrompido com estacionamento, o thread será suspenso; O nó após head usará tryAcquire para competir com o novo thread pelo bloqueio, indicando que a implementação do bloqueio aqui é injusta.
setHead vazio privado (nó nó)
Defina o nó como head e defina os atributos thread e prev como null para facilitar o GC. Na verdade, é equivalente a sair da fila do início da fila.
privado estático booleano deveriaParkAfterFailedAcquire (Nó pred, Nó nó)
Verifique e atualize o status do nó predecessor do nó que não conseguiu adquirir o bloqueio para SIGNAL e retorne se o thread atual precisa ser bloqueado.
Lógica de implementação: Determine o waitStatus do nó predecessor e retorne true quando for SIGNAL quando >0, significa que o nó predecessor foi cancelado e o nó predecessor será ignorado em um loop até o waitStatus de um nó predecessor; <=0 e, em seguida, retorne falso, caso contrário, CAS Defina o waitStatus do nó predecessor como SIGNAL e retorne falso;
parque booleano final privadoAndCheckInterrupt()
Estacionar chamada, verificar o status da interrupção e retornar
return thread.interrupted() O resultado retornado aqui é usado para determinar se o thread foi despertado devido a uma interrupção. Se foi despertado devido a uma interrupção, ele ainda estará no loop em adquireQueued e será bloqueado por park novamente ( porque o método interrompido() limpará o sinalizador de interrupção, para que o estacionamento possa ter efeito novamente quando despertado pelo desestacionamento, o bloqueio será adquirido quando pseudo-despertado, basta verificar novamente através do loop externo;
O que causa a falsa excitação?
private void cancelAcquire (nó nó)
Chamado quando todo o método lança uma exceção, esvazie o thread do Node, defina o status como CANCELLED e encontre o nó com waitStatus<=0 como o precursor, remova-se da fila e ative o seguinte, se necessário; um nó adequado
private void unparkSuccessor (nó nó)
Deve-se observar que se o próximo nó não estiver vazio, desestacione-o; se o próximo nó for nulo, comece da cauda e encontre o nó mais à frente com waitStatus<=0 que não é o nó da cauda até a cauda. a frente.
A razão pela qual pesquisamos de trás para frente é porque definir o antecessor e o sucessor de uma lista duplamente vinculada não é uma operação atômica. Pode acontecer que o próximo nó esteja vazio, mas já esteja na fila, ou o nó acabou de. foi cancelado e seu próximo aponta para si mesmo, e a travessia chega a este nó.
público final void adquirirInterruptivelmente(int arg)
Para obter o status de sincronização no modo exclusivo em resposta à interrupção, Thread.interrupted() será usado para determinar se ela será interrompida antes do tryAcqure. Em caso afirmativo, uma exceção será lançada.
tentar adquirir
tente uma vez primeiro
private void doAcquireInterruptably(int arg)
A lógica básica é a mesma de adquireQueued A diferença é que addWaiter é chamado internamente e não retorna se há uma interrupção do thread, mas lança diretamente uma InterruptedException após verificar se o parque foi despertado pela interrupção.
cancelar adquirir
público final booleano tryAcquireNanos(int arg, long nanosTimeout)
Aquisição exclusiva do estado de sincronização com timeout, respondendo a interrupções. Se o status de sincronização não for obtido dentro do período de tempo limite, será retornado falso.
tentar adquirir
tente uma vez primeiro
booleano privado doAcquireNanos(int arg, long nanosTimeout)
A lógica básica é a mesma do doAcquireInterruptably, com a adição da lógica de julgamento de tempo e o uso do limite spinForTimeoutThreshold.
cancelar adquirir
lançamento booleano final público (int arg)
O modo exclusivo libera o estado de sincronização. Primeiro, tryRelease é chamado para tentar modificar o estado. Após o sucesso, considera-se que head != null e head.waitStatus!=0 (o waitStatus de head aqui é 0, indicando uma fila vazia quando o waitStatus de head). é 0, significa uma fila vazia. Quando é chamado, unparkSuccessor é chamado para ativar os nós subsequentes e, em seguida, retorna true, falha e retorna false.
tryRelease booleano protegido (int arg)
Tente modificar o estado. Não há implementação específica no AQS e as subclasses precisam implementá-lo sozinhas. A variável arg do método release é usada para este método. O valor de retorno é se o estado foi liberado com sucesso;
Para obter uma descrição de arg, consulte a seção de implementação abaixo.
desestacionarSucessor
Modo de compartilhamento
Vários threads obtêm status de sincronização ao mesmo tempo
público final void adquirirShared(int arg)
O modo compartilhado adquire status de sincronização e ignora interrupções. Implementação: primeiro chame tryAcquireShared para tentar obter o status de sincronização. Após falha (o resultado é menor que 0), chame doAcquireShared para girar para obter o status de sincronização.
protegido int tryAcquireShared(int arg)
Tente obter o status de sincronização. Nenhum método específico é fornecido no AQS e é implementado por subclasses. Um valor de retorno menor que 0 indica que a aquisição falhou; igual a 0 indica que a aquisição foi bem-sucedida, mas nenhuma outra aquisição em modo compartilhado foi bem-sucedida maior que 0 indica que a aquisição foi bem-sucedida e outras aquisições em modo de compartilhamento também podem ser bem-sucedidas;
privado vazio doAcquireShared(int arg)
Depois de entrar na fila, o spin tenta obter o status de sincronização. A lógica básica é basicamente a mesma do adquireQueued no modo exclusivo. A diferença é que quando o antecessor do nó é o principal, o resultado retornado por tryAcquireShared precisa ser julgado. é maior ou igual a 0 (indicando que ainda há recursos, pode continuar a se propagar), então chame setHeadAndPropagate para definir os atributos de cabeçalho e propagação (defina um novo cabeçalho e julgue o nó sucessor, e ative-o se apropriado) ; caso contrário, você ainda precisará chamar shouldParkAfterFailedAcquire e parkAndCheckInterrupt para operações subsequentes (estacionamento e julgamento de interrupção subsequente, etc.). Além disso, o método selfTninterrupt para definir o status da interrupção também é executado neste método.
tenteAcquireShared
Quando o antecessor do nó for principal, tente primeiro.
private void setHeadAndPropagate (nó nó, propagação int)
A personificação da comunicação. O que esse método realmente verifica é o nó sucessor do nó que atualmente adquire o bloqueio, ou seja, ele pode ser propagado para trás uma vez. No início, eu estava me perguntando por que não encontrei nenhum código semelhante a acordar para trás em um loop. Mais tarde, descobri que o próprio setHeadAndPropagate está em for (;;). , então Isso atinge o propósito de propagação para trás, um por um. Observe que eles não despertam juntos e depois competem, eles despertam um por um.
Continue a ativar os nós para trás. Este método será chamado somente quando tryAcquireShared retornar o resultado r>=0 (indicando que ainda há recursos disponíveis) e r for passado para o método como um parâmetro de propagação. Este método definirá o nó como head e, em seguida, determinará se ele pode ser passado para trás. Em caso afirmativo, chame doReleaseShared para ativar o nó subsequente.
Lógica de julgamento: propagate>0 (indicando que há permissão) Ou cabeçalho anterior == null Ou head.waitStatus anterior <0 Ou o cabeçalho atual (ou seja, nó) == null Ou agora head.waitStatus <0 Então vá para a próxima etapa: nó.next == nulo ou node.next.isShared()
Na verdade não está muito claro: 1. Por que precisamos determinar a cabeça atual ((h = cabeça) == nulo) 2. Por que doReleaseShared ainda precisa ser feito após node.next == null
Especulação de 1: Como o head pode ser modificado por outros threads durante o processo de julgamento, se o novo head ainda atender a essa condição, você ainda pode continuar, sinto que a lógica do julgamento aqui é mais como não sei por que o head é nulo; mas ainda tento uma estratégia de processamento conservadora, então 2 pode ser uma estratégia conservadora semelhante.
Considera-se apenas que waitStatus<0 ocorre porque o status do cabeçote pode mudar para SIGNAL e CONDITION não aparecerá aqui.
vazio privado doReleaseShared()
Spin desperta o nó sucessor, que é diferente do modo independente porque também precisa garantir as propriedades de propagação. Implementação: Loop: Quando a fila não está vazia, determine o waitStatus de head. Quando for igual a SIGNAL, CAS define waitStatus como 0. Se for bem-sucedido, unparkSuccessor(head) será chamado para ativar o nó sucessor. , recomece desde o início; waitStatus é 0 e CAS define waitStatus como 0. Quando PROPAGATE falha, recomece; julgue head==h para ver se o nó principal foi modificado e, em caso afirmativo, saia do loop.
Observe que este método não passa parâmetros e começa diretamente do head.
Especulação: O nó de modo compartilhado pode ter apenas três estados: 0 (recém-criado), PROPAGATE e SIGNAL.
desestacionarSucessor
cancelar adquirir
público final void adquirirSharedInterruptably(int arg)
O modo compartilhado obtém o status de sincronização e suporta interrupções de maneira semelhante ao modo exclusivo.
tenteAcquireShared
private void doAcquireSharedInterruptably(int arg)
Semelhante à lógica doAcquireShared, uma exceção é lançada após uma interrupção ser detectada
cancelar adquirir
público final booleano tryAcquireSharedNanos(int arg, long nanosTimeout)
O modo compartilhado com período de tempo limite obtém o status de sincronização e suporta interrupção.
tenteAcquireShared
booleano privado doAcquireSharedNanos(int arg, long nanosTimeout)
A lógica de julgamento de tempo limite é semelhante ao modo exclusivo e outras lógicas de execução são semelhantes a doAcquireShared
cancelar adquirir
lançamento booleano final públicoCompartilhado (int arg)
O modo compartilhado libera o estado de sincronização
booleano protegido tryReleaseShared(int arg)
O método vazio, implementado por subclasses, tenta liberar o estado de sincronização. arg é o parâmetro de releaseShared e pode ser definido por você mesmo, o valor de retorno é verdadeiro quando este método libera com sucesso o estado de sincronização e outros threads podem adquirir, caso contrário, ele retorna falso;
doReleaseShared
Aqui ainda apenas despertamos um nó subsequente, e a propagação de despertar subsequente é realizada pelo nó despertado.
Como implementar seu próprio bloqueio baseado em AQS
recursos concorrentes
AQS usa a variável privada state para representar abstratamente os recursos para competição de bloqueio (por exemplo, 1 significa ocupado, 0 significa não ocupado)
As operações no estado requerem o uso de métodos relevantes: getState, setState, compareAndSetState, etc.
Método de implementação sob demanda
tryAcquire, tryAcquireShared, tryRelease, tryReleaseShared, isHeldExclusively
O parâmetro arg de vários métodos no AQS é finalmente passado para esses métodos, portanto, esses métodos determinam se o parâmetro arg é necessário e como usá-lo. Este é o significado de "pode representar qualquer coisa" nos comentários do código-fonte.
Bloqueio exclusivo (modo independente)
booleano protegido tryAcquire(int arg);
A implementação de consultas e a obtenção de recursos competitivos geralmente são concluídas pela operação CAS do estado. Os parâmetros de entrada podem ser definidos pelo desenvolvedor.
Exemplo simples: booleano protegido tryAcquire(int adquire) { assert adquire == 1; // Define que o parâmetro de entrada só pode ser 1; if (compareAndSetState(0, 1)) { retornar verdadeiro; } retorna falso; } booleano protegido tryRelease(int lançamentos) { assert releases == 1; // Define que o parâmetro de entrada só pode ser 1; if (getState() == 0) lançar new IllegalMonitorStateException(); setEstado(0); retornar verdadeiro; }
tryRelease booleano protegido (int arg);
Para liberar recursos concorrentes, geralmente a variável de estado é decrementada ou zerada. Os parâmetros de entrada podem ser definidos pelo desenvolvedor.
Não deve haver bloqueio ou espera dentro do método
bloqueio compartilhado
protegido int tryAcquireShared(int adquire)
Os parâmetros de entrada são definidos pelo desenvolvedor e podem ser considerados como o número de recursos que desejam obter; o valor de retorno int pode ser considerado como o número restante de recursos compartilhados;
Exemplo simples: protegido int tryAcquireShared(int adquire) {//não justo para (;;) { int disponível = getState(); int restante = disponível - adquire; if (restante < 0 || compareAndSetState(disponível, restante)) retorno restante; } } booleano protegido tryReleaseShared(int lançamentos) { para (;;) { int atual = getState(); int next = versões atuais; if (compareAndSetState(atual, próximo)) retornar verdadeiro; } }
booleano protegido tryReleaseShared(int lançamentos)
Os parâmetros de entrada são definidos pelos próprios desenvolvedores e podem ser considerados como a quantidade de recursos que eles desejam liberar.
variável de condição
Embora a lógica de implementação do AQS não use estado, ao usar variáveis de condição, o estado será usado como parâmetro de entrada de liberação (int arg) por padrão, portanto, muitas vezes é necessário modificar a variável de estado ao implementar esses métodos.
booleano protegido isHeldExclusively()
Retorna se o thread atual ocupa exclusivamente o bloqueio mutex correspondente à variável de condição
Métodos de implementação comuns: booleano protegido isHeldExclusive() { return getExclusiveOwnerThread() == Thread.currentThread(); } booleano protegido tryAcquire(int adquire) { ... setExclusiveOwnerThread(Thread.currentThread()); retornar verdadeiro; }
ReentranteReadWriteLock
ReentranteLock
Introdução
Os bloqueios reentrantes são divididos em dois métodos: bloqueios justos e bloqueios injustos.
implementar interface
Trancar
Serializável
classe interna
classe estática abstrata Sincronização
A classe abstrata é a base para a implementação do ReetrantLock
herdar
ResumoQueuedSynchronizer
método principal
bloqueio de vazio abstrato ()
É um método abstrato para adquirir um bloqueio? Precisa ser implementado por uma subclasse?
final booleano não justoTryAcquire(int adquire)
Implementação injusta de tryLock A implementação específica é: CAS adquire o bloqueio quando nenhum thread atualmente adquire o bloqueio. Se for bem-sucedido, o thread exclusivo de bloqueio será definido como o thread atual, se o thread atual já possuir o bloqueio, o número de bloqueios atuais será atualizado. Nestes dois casos, será retornado verdadeiro, indicando que o bloqueio está mantido, e falso será retornado nos demais casos.
Então, por que uma implementação injusta seria colocada na classe pai Sync? Como esse método é usado para bloqueios justos e bloqueios injustos, os tryLocks de ambos são baseados nesta implementação.
tryRelease booleano final protegido (int releases)
A implementação de tryRelease no AQS consiste em primeiro calcular o número total de bloqueios menos o número de bloqueios liberados para obter o número de bloqueios retidos restantes c. Se c for 0, limpe o estado, defina o estado para o valor de c e retorne; se nenhum bloqueio é mantido.
booleano final protegido isHeldExclusively()
isHeldExclusivamente implementado em AQS, retorne getExclusiveOwnerThread() == Thread.currentThread()
final int getHoldCount()
Retorna quantos bloqueios o thread atual contém. Este método depende do julgamento de isHeldExclusively
booleano final isLocked()
return getState() != 0; Se um thread mantém o bloqueio
Tópico final getOwner()
Retorna o thread que contém o bloqueio ou nulo se não
final ConditionObject novaCondição()
novoCondiçãoObject()
privado void readObject(java.io.ObjectInputStreams)
Método de desserialização, defina o estado como 0
classe final estática FairSync
Classe de implementação de bloqueio justo
herdar
Sincronizar
método principal
bloqueio de vazio final()
adquirir(1)
O parâmetro 1 aqui tem significado prático, que é diferente de CountDownLatch.
booleano final protegido tryAcquire(int adquire)
Implementação TryAcquire de fair lock. O processo de implementação é: quando o bloqueio ocupado c é 0, se não houver thread na frente e cas definir c para aquisição com sucesso, então defina o thread atual como um thread exclusivo e retorne verdadeiro se o thread atual já contém o bloqueio, atualiza o número de bloqueios e retorna verdadeiro. Retorna falso em todos os outros casos;
hasQueuedPredecessores
Há mais julgamento aqui do que bloqueio injusto. Mesmo que nenhum thread adquira o bloqueio no momento, se houver um thread esperando por mais tempo, ele desistirá da aquisição do bloqueio.
classe final estática NonfairSync
Classe de implementação de bloqueio injusto
herdar
Sincronizar
método principal
bloqueio de vazio final()
Primeiro tente cas(0,1) direto. Se for bem-sucedido, significa que ninguém mais está adquirindo o bloqueio. O thread atual capturou o bloqueio com sucesso e, em seguida, defina o thread exclusivo como o thread atual, se falhar. (1)
Na verdade, o método tryAcquire do bloqueio injusto também tentará cas. O caso direto aqui pode ser para destacar as chamadas anteriores que podem obter recursos mais cedo.
booleano final protegido tryAcquire(int adquire)
retornar não justoTentarAcquire(adquire) A implementação injusta já existe no Sync, ligue diretamente
Variáveis de membro
sincronização final privada
Indica se o bloqueio é implementado de forma justa ou injusta e corresponde a FairSync ou NonfairSync após a instanciação.
Construtor
public ReentrantLock()
Bloqueio injusto construído por padrão
public ReentrantLock (booleano justo)
Construir bloqueio justo ou bloqueio injusto de acordo com a escolha justa
método principal
bloqueio de vazio público ()
sincronizar.lock()
public void lockInterruptivelmente()
sincronizar.acquireInterruptivelmente(1)
tryLock booleano público()
return sync.nonfairTryAcquire(1); Se o bloqueio for adquirido com sucesso ou o thread atual já contém o bloqueio, ele retorna verdadeiro, caso contrário, retorna falso.
Não importa qual seja o bloqueio aqui, é uma tentativa injusta de adquiri-lo.
public boolean tryLock (tempo limite longo, unidade TimeUnit)
retornar sincronizar.tryAcquireNanos(1, unit.toNanos(tempo limite))
tryAcquireNano chama tryAcquire, portanto há uma diferença entre justiça e injustiça.
desbloqueio por vazio público()
sincronização.release(1)
Condição pública novaCondição()
retornar sincronização.newCondition()
público int getHoldCount()
return sync.getHoldCount() O número de bloqueios mantidos pelo thread atual
booleano público isHeldByCurrentThread()
retorne sync.isHeldExclusively() se o thread atual mantém o bloqueio
booleano público isLocked()
retornar sincronização.isLocked()
público final booleano isFair()
retornar instância de sincronização do FairSync
Thread protegido getOwner()
retornar sincronizar.getOwner()
público final booleano hasQueuedThreads()
retorne sync.hasQueuedThreads() se há threads na fila
público final booleano hasQueuedThread (thread thread)
return sync.isQueued(thread); Se o thread atual está na fila
público final int getQueueLength()
retornar sincronização.getQueueLength();
Coleção protegida<Thread> getQueuedThreads()
retornar sincronização.getQueuedThreads();
hasWaiters booleano público (condição)
Se há threads aguardando uma determinada condição (correspondente a haver threads no estado CONDITION na fila de condições)
public int getWaitQueueLength(Condição condição)
O número de threads no estado CONDITION na fila de condições para uma determinada condição
coleção protegida<Thread> getWaitingThreads(Condição condição)
Doença
Introdução
Variáveis de condição de interface são usadas principalmente para gerenciar a dependência da execução do thread em determinados estados, mapear os métodos de monitor do objeto (wait/notify/notifyAll) para objetos diretos e combiná-los com o uso de qualquer implementação de Lock para atingir cada objeto. o efeito de vários conjuntos de espera. É equivalente a Lock substituindo sincronizar e Condition substituindo o método monitor, ou seja, Condition precisa ser usado em conjunto com Lock.
As principais diferenças entre os métodos Condition e Monitor são: 1. A condição oferece suporte a várias filas de espera e uma instância de Lock pode ser vinculada a várias condições. 2. A condição pode suportar configurações de interrupção de resposta e tempo limite
O significado profundo de vincular múltiplas condições é que os threads podem ser gerenciados de acordo com diferentes situações. O chamado controle "mais refinado" significa que os threads bloqueados devido a diferentes condições estão em filas de condições diferentes e só serão despertados quando precisarem ser despertados. Entrar na fila de sincronização para competir, por si só diferencia e isola.
entender
A diferença da competição geral de bloqueios é que a condição enfatiza as condições. Em um ambiente multithread, as condições precisam ser atendidas antes que certas operações possam ser executadas, enquanto a competição geral de bloqueios é equivalente a competir pelo direito de executar um determinado trecho de código; .
Modelo de programação usual: Obtenha bloqueio; while (estado condicional não satisfeito) { liberar bloqueio; O encadeamento trava e aguarda até que a condição seja atendida para notificação; Readquira a fechadura; } Operações de seções críticas; liberar bloqueio;
Relacionamento ternário: bloqueio, espera, asserção condicional (predicado). Cada chamada de espera está implicitamente associada a uma asserção condicional específica; ao chamar wait para uma asserção condicional específica, o chamador já deve conter o bloqueio da fila condicional relacionada à asserção, e esse bloqueio deve proteger as condições que constituem a asserção condicional. Variáveis de Estado
predicado: uma função que retorna o tipo bool
criar
Criada através do método newCondition da interface Lock, a classe que precisa ser implementada implementa sua própria lógica (essencialmente criando uma instância de ConditionObject)
método principal
vazio aguardar()
Deixe o thread esperar até ser despertado por sinal ou interrupção quando chamado, o bloqueio associado à condição será liberado automaticamente e o thread irá parar de executar a tarefa atual até ser despertado por sinal/sinalAll/interrupção/despertar espúrio. O thread precisa tentar novamente adquirir o bloqueio associado à condição antes de chamar o método await.
espera booleana (muito tempo, unidade TimeUnit)
Semelhante ao awaitNanos, você pode especificar a unidade de tempo e retornar falso quando o tempo acabar, caso contrário, retornar verdadeiro
longa esperaNanos(longo nanosTimeout)
Semelhante a aguardar, mas irá acordar após o horário especificado e retornar o tempo não utilizado
void aguarda ininterruptamente();
Semelhante a aguardar, mas não responde a interrupções
booleano waitUntil (Data limite)
Semelhante ao awaitNanos, a forma de especificar o tempo é diferente. Se o prazo for atingido, ele retorna falso, caso contrário, retorna verdadeiro.
sinal vazio()
Acorde um thread em estado de espera Depois que o thread for ativado, ele precisará adquirir o bloqueio novamente.
sinal vazioTodos()
Acorde todos os threads em espera
Trancar
Introdução
A interface fornece alguns métodos comuns para solicitar/liberar bloqueios
Comparação com sincronizado
Os mecanismos relacionados de sincronização (como objetos de monitoramento, etc.) são integrados e não podem ser acessados no nível do idioma. Lock é uma solução alternativa no nível do idioma, para que você possa obter algumas conveniências no nível do idioma (como saber). se o bloqueio foi adquirido com sucesso, etc.)
O processo de bloqueio e desbloqueio sincronizado é concluído pela própria palavra-chave, enquanto Lock precisa chamar explicitamente o método de bloqueio e desbloqueio.
O bloqueio pode usar variáveis de condição para usar bloqueios de maneira mais flexível
Lock pode responder a interrupções, mas sincronizado não pode
método principal
bloqueio vazio()
void lockInterruptivelmente()
As interrupções serão respondidas durante o processo de solicitação de bloqueio.
booleano tryLock()
Tente adquirir o bloqueio e retornar o resultado diretamente (sem bloquear o thread atual)
boolean tryLock(muito tempo, unidade TimeUnit)
tryLock com tempo limite e interrupção de resposta
void desbloquear();
Condição novaCondição()
Retorna um objeto Condition associado ao bloqueio
Suporte de bloqueio
Introdução
Primitivas básicas de bloqueio de thread (relacionadas a permissões) usadas para criar bloqueios e outras classes de sincronização
A camada inferior depende da implementação insegura
Método de construção
Privado, não é permitida a construção de objetos desta classe
Variáveis de membro
privado estático final sun.misc.Unsafe INSEGURO
Construir em bloco de código estático
sun.misc.Inseguro
privado estático final longo parkBlockerOffset
Obtenha a posição de deslocamento do atributo parkBlocker no objeto da classe Thread por meio de Unsafe
parkBlocker é um registro quando o thread está bloqueado, o que facilita ferramentas de monitoramento e diagnóstico para identificar o motivo do bloqueio do thread, é nulo quando o thread não está bloqueado;
privado estático final longo SEED
Obtenha a posição de deslocamento da propriedade threadLocalRandomSeed na classe Thread por meio de Unsafe
privado estático final longo PROBE
Obtenha a posição de deslocamento da propriedade threadLocalRandomProbe na classe Thread por meio de Unsafe
privado estático final longo SECUNDÁRIO
Obtenha a posição de deslocamento da propriedade threadLocalRandomSecondarySeed na classe Thread por meio de Unsafe
Essas três propriedades são definidas por ThreadLocalRandom
método principal
objeto estático público getBlocker (Thread t)
Obtenha o objeto parkBlocker no objeto Thread por meio de Unsafe
private static void setBlocker (Thread t, Object arg)
Método privado, defina o objeto parkBlocker em Thread through Unsafe, os seguintes métodos de configuração do bloqueador chamam este método
parque vazio estático público()
Bloqueie o thread e aguarde “permit” para continuar a execução.
Implementação específica (Hotspot): classe Parker
具体的实现与平台相关,每个线程都有一个Parker实例,在linux实现中实际上是以mutex和condition最终实现了锁和阻塞的过程(具体看笔记文章,这里不写了)
主要成员变量(linux实现)
private volatile int _counter
表示许可,大于0表示已获取许可,每次park只会将其设置为1,不会递增
Portanto, chamar unpark duas vezes e depois chamar park duas vezes ainda bloqueará a segunda vez.
protected pthread_mutex_t _mutex[1]
互斥变量
在代码中可以通过linux原子指令pthread_mutex_lock、pthread_mutex_trylock、pthread_mutex_unlock对变量进行加锁和解锁操作
protected pthread_cond_t _cond[1]
条件变量
利用线程间共享的全局变量进行同步的一种机制,一般和互斥锁结合使用(避免条件变量的竞争);可以通过pthread_cond_wait、pthread_cond_timedwait等指令进行操作
Também existe uma fila de espera no kernel do Linux para gerenciar processos bloqueados
方法
park
unpark
Diferenças no uso da espera
wait precisa aguardar a notificação antes de poder ser ativado. Há um processo sequencial e o unpark que o parque está aguardando pode ser chamado antes do estacionamento. Nesse momento, o programa após o estacionamento pode continuar a ser executado.
wait precisa pegar o monitor do objeto, mas park não
Perceber
Se ocorrer uma interrupção no estacionamento, o estacionamento também retornará, mas não lançará uma exceção de interrupção.
parque vazio estático público (bloqueador de objetos)
Devido à função do parkBlocker, é mais recomendado usar o método park com parâmetros do bloqueador. Após usá-lo, você pode ver o prompt em qual objeto o thread está bloqueado por meio de ferramentas de diagnóstico como jstack, como estacionamento para esperar. <0x000000045ed12298> (um objeto xxxx com todo nome qualificado)
public static void parkNanos (bloqueador de objetos, nanos longos)
parque vazio estático públicoNanos (nanos longos)
O tempo máximo de bloqueio antes que o programa continue a execução
public static void parkUntil (bloqueador de objetos, prazo longo)
public static void parkUntil (longo prazo)
public static void unpark (thread thread)