O que é Java Multithreading
O Java fornece um mecanismo para lidar com várias tarefas simultaneamente (simultaneamente e independentemente). Vários threads coexistem no mesmo processo JVM, para que compartilhem o mesmo espaço de memória. Comparado com vários processos, a comunicação entre vários threads é mais leve. No meu entendimento, o Java Multithreading é inteiramente para melhorar a utilização da CPU. Os threads java têm 4 estados: novos, executáveis, bloqueados e mortos. A chave é bloquear. Bloquear significa esperar. O encadeamento de bloqueio não participa da alocação de fatias de tempo do despachante do encadeamento, então, naturalmente, não usará a CPU. Em um ambiente multithread, esses threads não bloqueados são executados e fazem uso total da CPU.
Um resumo de 40 perguntas
1. Qual é o uso do multi-threading?
Uma pergunta que pode parecer bobagem para muitas pessoas: eu posso usar apenas multi-threading, mas qual é a utilidade? Na minha opinião, essa resposta é ainda mais absurda. O chamado "saber o que é verdade" é "saber o que é verdade", "sabendo o que é verdade", "por que usar" é "saber o que é verdade". Somente atingindo o nível de "saber o que é verdadeiro" é considerado capaz de aplicar um ponto de conhecimento livremente. Ok, deixe -me dizer o que penso sobre esse problema:
(1) Dê um jogo completo às vantagens da CPU multi-núcleo
Com o avanço da indústria, os laptops de hoje, os desktops e até os servidores de aplicativos comerciais são todos de núcleo duplo e 4 núcleos, 8 núcleos ou mesmo 16 núcleos não são incomuns. Se for um programa de tiro único, 50% é desperdiçado na CPU de núcleo duplo e 75% é desperdiçado na CPU de 4 núcleos. A chamada "multi-threading" em uma CPU de núcleo único é falsa multi-threading. O processador processará apenas uma lógica ao mesmo tempo, mas os threads alternam relativamente rapidamente, que parece que vários threads estão em execução "ao mesmo tempo". Multithreading em CPUs multi-núcleo é a verdadeira multithreading. Ele pode permitir que sua lógica de vários segmentos funcione ao mesmo tempo e multi-threading. Ele pode realmente dar um jogo completo às vantagens das CPUs com vários núcleos para atingir o objetivo de fazer pleno uso da CPU.
(2) impedir o bloqueio
Do ponto de vista da eficiência da operação do programa, uma CPU de núcleo único não apenas dará um jogo completo às vantagens da multi-threading, mas também reduzirá a eficiência geral do programa porque a execução de múltiplas fiéis em uma CPU de núcle causará a troca de contexto de roscas, o que reduzirá a eficiência geral do programa. No entanto, ainda precisamos aplicar multithreading às CPUs de núcleo único para evitar o bloqueio. Imagine, se uma CPU de núcleo único usar um único thread, desde que o thread esteja bloqueado, por exemplo, lendo remotamente um determinado dados, o par não retornará há muito tempo e não definiu um tempo limite, todo o seu programa parará de executar antes que os dados sejam retornados. Multi-threading pode impedir esse problema. Vários tópicos são executados ao mesmo tempo. Mesmo que o código de um encadeamento esteja bloqueado da leitura de dados, ele não afetará a execução de outras tarefas.
(3) Fácil de modelar
Esta é outra vantagem que não é tão óbvia. Suponha que exista uma grande tarefa A, programação de thread única, então você precisa considerar muito e é mais problemático criar todo o modelo de programa. No entanto, se você quebrar essa grande tarefa A em várias pequenas tarefas, a tarefa B, a tarefa C e a Tarefa D, criar um modelo de programa separadamente e executar essas tarefas separadamente através de vários threads, será muito mais simples.
2. Como criar tópicos
Um problema mais comum é geralmente dois:
(1) herdar a classe de threads
(2) Implementar a interface executável
Quanto ao que é melhor, não é preciso dizer que o último é definitivamente melhor, porque a maneira de implementar interfaces é mais flexível que o método da classe de herança e também pode reduzir o acoplamento entre os programas. A programação orientada para interface também é o núcleo dos seis principais princípios dos padrões de design.
3. A diferença entre o método start () e run ()
Somente quando o método start () for chamado, as características da multi-threading serão exibidas e o código no método run () de diferentes encadeamentos será executado alternadamente. Se você apenas chamar o método run (), o código será executado de maneira síncrona. Você deve aguardar o código no método run () de um thread a ser executado antes que outro thread possa executar o código no método run ().
4. A diferença entre interface executável e interface chamada
Há uma pergunta profunda e também mostra a amplitude do conhecimento aprendido por um programador Java.
O valor de retorno do método run () na interface executável é nulo e o que ele faz é apenas executar o código no método run (); O método Call () na interface chamável possui um valor de retorno, que é genérico, e pode ser usado para obter os resultados da execução assíncrona em conjunto com o Future e o FutureTask.
Esse é realmente um recurso muito útil, porque uma das principais razões pelas quais o Multithreading é mais difícil e mais complexo do que o rosqueamento único é que o multithreading é cheio de incógnitas. Um certo tópico foi executado? Há quanto tempo um tópico foi executado? Os dados que esperamos quando um thread são executados? Não se sabe que tudo o que podemos fazer é aguardar a execução dessa tarefa multitreada. Callable+Future/FutureTask pode obter os resultados da corrida com vários threads e pode cancelar a tarefa do thread quando o tempo de espera for muito longo e os dados necessários não forem obtidos. É realmente útil.
5. A diferença entre CyclicBarrier e Countdownlatch
Ambas as classes que parecem um pouco semelhantes podem ser usadas para indicar que o código é executado em um determinado ponto em java.util.concurrent. A diferença entre os dois é:
(1) Depois que um thread de ciclicbarrier é executado em um determinado ponto, o thread para de funcionar. Até que todos os threads atinjam esse ponto, todos os threads não serão executados novamente; Countdownlatch não é. Depois que um thread é executado em um determinado ponto, ele apenas fornece um certo valor -1 e o thread continua a ser executado.
(2) CyclicBarrier só pode evocar uma tarefa, o CountdownLatch pode evocar várias tarefas
(3) O CyclicBarrier pode ser reutilizado, o CountdownLatch não pode ser reutilizado e o valor da contagem é 0 não será reutilizado, e o CountdownLatch não será usado novamente
6. O papel das palavras -chave voláteis
Uma questão muito importante é que todo programador Java que aprende e aplica multi-threading deve dominá-lo. O pré -requisito para entender o papel da palavra -chave volátil é entender o modelo de memória Java. Não vou falar sobre o modelo de memória Java aqui. Você pode se referir ao ponto 31. Existem duas funções principais da palavra -chave volátil:
(1) O multithreading gira principalmente em torno das duas características de visibilidade e atomicidade. As variáveis modificadas pela palavra -chave volátil garantem sua visibilidade entre vários threads, ou seja, toda vez que a variável volátil é lida, deve ser os dados mais recentes.
(2) A execução do código subjacente não é tão simples quanto a linguagem de alto nível que vemos - programas Java. Sua execução é o código Java -> bytecode -> Execute o código C/C ++ correspondente de acordo com o código Bytecode -> C/C ++ é compilado na linguagem de montagem -> interagindo com o circuito de hardware. Na realidade, para obter melhor desempenho, a JVM pode reordenar as instruções e alguns problemas inesperados podem surgir sob multi-threading. O uso do Volátil proibirá a reordenação semântica, o que obviamente reduz a eficiência da execução do código até certo ponto
Do ponto de vista prático, um papel importante do volátil é combinar com CAS para garantir a atomicidade. Para detalhes, você pode consultar as classes no pacote java.util.concurrent.atomic, como o AtomicInteger.
7. O que é a segurança do fio
Outra pergunta teórica, há muitas respostas. Gostaria de dar um que, pessoalmente, acho que é a melhor explicação: se o seu código sempre puder obter o mesmo resultado quando executado em multithreading e threading, seu código será seguro.
Há algo que vale a pena mencionar sobre esse problema, ou seja, existem vários níveis de segurança de threads:
(1) imutável
Como string, número inteiro, longo, etc., todos são tipos finais. Nenhum fio pode alterar seus valores. Se você quiser alterá -los, não criará um novo. Portanto, esses objetos imutáveis podem ser usados diretamente em um ambiente multithread sem meios de sincronização.
(2) segurança absoluta de threads
Independentemente do ambiente de tempo de execução, o chamador não precisa de medidas adicionais de sincronização. Para fazer isso, você geralmente precisa pagar muitos custos extras. Em Java, na verdade não são classes seguras de threads. No entanto, também existem classes que são absolutamente seguras de threads, como copywritearraylist e copywritearrayset
(3) segurança relativa do fio
Segurança de rosca relativa é o que geralmente chamamos de segurança de threads. Por exemplo, o vetor, adicionar e remover métodos são operações atômicas e não será interrompido, mas está limitado a isso. Se um encadeamento atravessando um determinado vetor, um encadeamento é adicionado ao mesmo tempo, ocorrerá uma exceção concorrente em 99% dos casos, que é o mecanismo de falha.
(4) threads não são seguros
Não há nada a dizer sobre isso. Arraylist, LinkedList, Hashmap, etc. são todas as classes não-thread.
8. Como obter o arquivo de despejo de threads em java
Para problemas como Loop Dead, Deadlock, Blocking, Slow Page, etc., pressionar o despejo de threads é a melhor maneira de resolver o problema. O chamado despejo de rosca é a pilha de roscas. Existem duas etapas para obter a pilha de threads:
(1) Obtenha o PID do thread, você pode usar o comando jps e também pode usar o ps -ef | Java Grep no ambiente Linux
(2) Imprima a pilha de threads, você pode usar o comando jstack pid e também pode usar o kill -3 pid no ambiente Linux
Além disso, a classe Thread fornece um método getStackTrace () que também pode ser usado para obter a pilha de thread. Este é um método de instância; portanto, esse método está vinculado a uma instância de encadeamento específica. Cada vez que você obtém a pilha atualmente em execução em um tópico específico.
9. O que acontece se um thread tiver uma exceção de tempo de execução?
Se essa exceção não for capturada, o thread parará de executar. Outro ponto importante é: se este tópico mantiver um monitor de um determinado objeto, o monitor de objeto será liberado imediatamente
10. Como compartilhar dados entre dois threads
Basta compartilhar objetos entre os threads e depois evocar e aguardar a espera/notificar/notificarl, aguardar/sinalizar/sinalizar. Por exemplo, o bloqueio de bloqueio de fila é projetado para compartilhar dados entre threads.
11. Qual é a diferença entre o método do sono e o método de espera
Esta pergunta é feita com frequência, o método do sono e o método de espera podem ser usados para desistir da CPU por um certo período de tempo. A diferença é que, se o thread segurar o monitor de um objeto, o método do sono não desistirá do monitor desse objeto e o método de espera desistirá do monitor desse objeto.
12. Qual é o papel do modelo de consumidor do produtor?
Esta questão é teórica, mas importante:
(1) Melhorar a eficiência operacional de todo o sistema, equilibrando a capacidade de produção dos produtores e da capacidade de consumo dos consumidores, que é o papel mais importante do modelo de consumidor do produtor.
(2) Desaparelamento, esta é a função acompanhada pelo produtor e modelo de consumidor. A desacoplamento significa que há menos conexões entre produtores e consumidores. Quanto menos conexões, mais eles podem se desenvolver por conta própria sem receber restrições mútuas.
13. Qual é o uso do Threadlocal
Simplificando, Threadlocal é uma prática de trocar espaço pelo tempo. Cada encadeamento mantém um ThreadLocal.ThreadLocalMap implementado pelo método de endereço aberto para isolar os dados e não compartilhar os dados; portanto, naturalmente não haverá problemas de segurança de threads.
14. Por que esperar () e notificar ()/notifyAll () métodos são chamados no bloco de sincronização
Isso é forçado pelo JDK. O método wait () e notify ()/notifyAll () deve primeiro obter o bloqueio do objeto antes de ligar
15. Qual é a diferença entre o método wait () e notify ()/notifyAll () ao abandonar o monitor de objeto
A diferença entre o método wait () e o método notify ()/notifyAll () ao desistir do monitor de objeto é que o método wait () libera imediatamente o monitor de objeto, enquanto o método notify ()/notifyAll () aguardará o código restante do encadeamento a ser executado antes de desistir do monitor de objeto.
16. Por que usar o pool de threads
Evite a criação e destruição frequentes dos threads para alcançar a reutilização de objetos de thread. Além disso, o uso de pools de threads também pode controlar com flexibilidade o número de simultaneidade de acordo com o projeto.
17. Como detectar se um thread segura um monitor de objeto
Também vi uma pergunta de entrevista multithread na Internet para saber que existe uma maneira de determinar se um thread segura um monitor de objeto: a classe Thread fornece um método HoldSlock (Object OBJ), que retornará verdadeiro se e somente se o monitor do objeto Obj for mantido por um thread. Observe que este é um método estático, o que significa que "um determinado thread" refere -se ao thread atual.
18. A diferença entre sincronizado e reentrantlock
O sincronizado é a mesma palavra -chave que se, senão, para, e enquanto, e Reentrantlock é uma classe, que é a diferença essencial entre os dois. Como o ReentrantLock é uma classe, ele fornece recursos cada vez mais flexíveis do que o sincronizado, que pode ser herdado, possui métodos e possui várias variáveis de classe. A escalabilidade do reentrantlock do que o sincronizado é refletida em vários pontos:
(1) Reentrantlock pode definir o tempo de espera para adquirir a fechadura, evitando o impasse
(2) Reentrantlock pode obter várias informações de bloqueio
(3) Reentrantlock pode implementar de maneira flexível a notificação multicanal
Além disso, os mecanismos de travamento dos dois são realmente diferentes. O reentrantlock subjacente chama o método do parque da UNSAFE para travar, e a operação sincronizada deve ser a palavra Mark no cabeçalho do objeto, não posso ter certeza disso.
19. Qual é a simultaneidade do concorrente
A simultaneidade do concorrente é o tamanho do segmento, que é 16 por padrão, o que significa que no máximo 16 threads podem operar simultaneamente ao mesmo tempo. Esta também é a maior vantagem de simplificar o Hashtable. De qualquer forma, a hashtable pode ter dois threads ao mesmo tempo obtendo os dados em hashtable?
20. O que é readwritelock
Primeiro de tudo, vamos deixar claro que não é que o Reentrantlock não seja bom, é apenas que o Reentrantlock seja limitado em alguns momentos. Se o ReentrantLock for usado, pode ser para impedir que as inconsistências de dados causadas por thread a dados de escrita e thread b lendo dados. No entanto, dessa maneira, se o thread C lendo os dados e o thread D também estiver lendo dados, a leitura de dados não alterará os dados. Não há necessidade de travá -lo, mas ainda o trava, o que reduz o desempenho do programa.
Por esse motivo, nasceu o Lock Lock ReadWritelock. ReadWritelock é uma interface de bloqueio de leitura e gravação. ReentrantreadWritelock é uma implementação concreta da interface ReadWritelock, que realiza a separação de leitura e gravação. Os bloqueios de leitura são compartilhados e os bloqueios de gravação são exclusivos. Leia e leitura e leitura não serão mutuamente exclusivas. Leia e escreva, escreva e leia, escreva e escreva serão mutuamente exclusivos, melhorando o desempenho de leitura e gravação.
21. O que é FutureTask
Isso é realmente mencionado anteriormente. FutureTask representa uma tarefa de operação assíncrona. Uma classe de implementação específica de chamadas pode ser transmitida para o FutureTask, que pode aguardar o resultado dessa operação assíncrona para obter, determinar se ela foi concluída e cancelar a tarefa. Obviamente, como o FutureTask também é uma classe de implementação da interface executável, o FutureTask também pode ser colocado no pool de threads.
22. Como descobrir qual thread usa a CPU mais longa no ambiente Linux
Este é um problema mais prático, e acho que esse problema é bastante significativo. Você pode fazer isso:
(1) Obtenha o PID, JPS ou PS -EF do projeto | Java Grep, que foi mencionado antes
(2) Top -h -p pid, a ordem não pode ser alterada
Isso imprimirá a porcentagem do tempo da CPU que o projeto atual leva para cada thread. Observe que o aqui é LWP, que é o número do thread do thread nativo do sistema operacional. Minha montanha de notebook não implanta projetos Java no ambiente Linux; portanto, não há como tirar capturas de tela e demonstrações. Se a empresa estiver implantando um projeto usando um ambiente Linux, você poderá experimentá -lo.
O uso de "Top -h -p Pid" + "JPS PID" pode facilmente encontrar uma pilha de roscas que ocupa uma CPU alta, posicionando assim o motivo da ocupação da CPU alta, que geralmente se deve a operações de código inadequadas que levam a um loop morto.
Finalmente, deixe -me mencionar que o LWP tocou com "top -h -p pid" é decimal, e o número do tópico local tocado com "JPS PID" é hexadecimal. Após a conversão, você pode localizar a pilha de encadeamento atual que ocupa uma CPU alta.
23. Programação Java Escreva um programa que cause impasse
Eu vi essa pergunta pela primeira vez e achei que era uma pergunta muito boa. Muitas pessoas sabem como é o impasse: Thread A e Thread B estão esperando as fechaduras um do outro causar um loop morto infinito para continuar o programa. Claro, é limitado apenas a isso. Se você perguntar como escrever um programa de impasse, você não saberá. Para ser franco, você não entende o que é um impasse. Se você entende uma teoria, terminará. Você basicamente não pode ver o problema do impasse na prática.
Para entender verdadeiramente o que é um impasse, essa questão não é difícil, existem algumas etapas:
(1) Dois threads seguram dois objetos de objeto: Lock1 e Lock2, respectivamente. Esses dois bloqueios servem como bloqueios para blocos de código síncrono;
(2) No método de execução () do thread 1, o bloco de código de sincronização obtém primeiro o bloqueio do objeto de Lock1, Thread.Sleep (XXX), o tempo não leva muito, 50 milissegundos são quase os mesmos e, em seguida, obtém o bloqueio do objeto do Lock2. Isso é feito principalmente para impedir que o encadeamento 1 obtenha continuamente os bloqueios de objetos de dois objetos: Lock1 e Lock2.
(3) Execução do encadeamento 2) (No método, o bloco de código de sincronização obtém primeiro o bloqueio do objeto2 e, em seguida, adquire o bloqueio do objeto1. É claro que o objeto Lock1 já está mantido pelo Thread 1 Lock e o Thread 2 deve aguardar o encadeamento 1 para liberar o objeto Lock1.
Dessa forma, após o thread 1 "dorme" e o thread 2 adquiriu o objeto Lock2. O thread 1 tenta adquirir o objeto Lock2 neste momento e está bloqueado. Neste momento, um impasse é formado. Não vou mais escrever o código, ele ocupa muito espaço. Java Multithreading 7: Deadlock Este artigo contém a implementação do código das etapas acima.
24. Como acordar um fio de bloqueio
Se o thread bloquear os métodos de chamada Wait (), Sleep () ou junção (), ele poderá interromper o thread e acordá -lo jogando uma interrupção; Se o thread encontrar bloqueio de IO, ele não será impotente porque o IO é implementado pelo sistema operacional e o código Java não poderá entrar em contato diretamente com o sistema operacional.
25. Que ajuda os objetos imutáveis ajudam a multithreading
Um problema mencionado acima é que objetos imutáveis garantem a visibilidade da memória dos objetos e não há necessidade de sincronização adicional para ler objetos imutáveis, o que melhora a eficiência da execução do código.
26. O que é a troca de contexto multithread
A comutação de contexto multithread refere -se ao processo de troca de controle da CPU de um encadeamento em execução para outro thread pronto e aguardando os direitos de execução da CPU a serem obtidos.
27. Se a fila do pool de threads estiver cheia quando você enviar uma tarefa, o que acontecerá neste momento
Se você estiver usando o LinkedBlockingQueue, ou seja, filas ilimitadas, isso não importa. Continue adicionando tarefas à fila de bloqueio e aguarde a execução, porque o LinkedBlockingQueue pode ser quase considerado uma fila infinita e pode armazenar tarefas infinitamente; Se você estiver usando uma fila limitada, por exemplo, ArrayBlockingQueue, a tarefa será adicionada primeiro ao ArrayBlockingQueue. Se o ArrayBlockingQueue estiver cheio, o rejeição de rejeição do Handler usará a política de rejeição para lidar com as tarefas completas, e o padrão será abortpolicy.
28. Qual é o algoritmo de agendamento de threads usado em Java?
Estilo preventivo. Depois que um thread usa a CPU UP, o sistema operacional calculará uma prioridade total com base em dados como prioridade do encadeamento, fome do encadeamento etc. e alocará na próxima vez em que uma fatia em um thread para execução.
29. Qual é a função do Thread.Sleep (0)
Esta pergunta está relacionada à pergunta acima, e estou todos juntos. Como o Java usa o algoritmo de agendamento de encadeamentos preventivos, pode ocorrer que um encadeamento geralmente obtenha controle da CPU. Para permitir alguns threads com prioridade relativamente baixa para obter controle da CPU, Thread.Sleep (0) pode ser usado para acionar manualmente o sistema operacional alocar fatias de tempo, que também é uma operação para equilibrar o controle da CPU.
30. O que é giro
Muitos códigos sincronizados são apenas um código muito simples, e o tempo de execução é muito rápido. O bloqueio dos threads esperando neste momento pode ser uma operação não que vale a pena, porque o bloqueio de threads envolve o estado de usuário e a comutação do estado do kernel. Como o código em sincronizado é executado muito rápido, você também pode deixar o thread aguardar que o bloqueio não seja bloqueado, mas, em vez disso, faça loops ocupados no limite do sincronizado. Isso é giro. Se você fez vários loops ocupados e descobre que o bloqueio não foi obtido e, em seguida, bloqueie -o, essa pode ser uma estratégia melhor.
31. O que é modelo de memória java
O modelo de memória Java define uma especificação para acesso de múltiplas threading à memória Java. O modelo de memória Java precisa ser totalmente explicado, mas não posso explicar claramente em algumas frases aqui. Deixe -me resumir brevemente.
Várias partes do modelo de memória Java:
(1) O modelo de memória Java divide a memória na memória principal e na memória de trabalho. O estado da classe, ou seja, as variáveis compartilhadas entre as classes, são armazenadas na memória principal. Toda vez que um thread Java usa essas variáveis na memória principal, ele lerá as variáveis na memória principal e as deixa existir em sua própria memória de trabalho. Ao executar seu próprio código de threads, ele usa essas variáveis e opera a de sua própria memória de trabalho. Após a execução do código do encadeamento, o valor mais recente será atualizado para a memória principal.
(2) Várias operações atômicas são definidas para operar variáveis na memória principal e na memória de trabalho
(3) Defina as regras para o uso de variáveis voláteis
(4) Acontece, ou seja, ou seja, o princípio da primeira ocorrência, define algumas regras nas quais a operação deve ocorrer primeiro na operação B. Por exemplo, o código na frente do fluxo de controle no mesmo fio deve ocorrer primeiro no código por trás do fluxo de controle, a ação de liberação do Lock Desbloqueio deve ocorrer primeiro na ação do travamento do mesmo, etc. Se um determinado pedaço de código não cumprir com todas as regras, então esse código de código deve ser um thread não é seguro.
32. O que é CAS
CAS, Nome completo Compare e Set, é comparado. Suponha que existam três operandos: valor da memória v, o valor antigo esperado a, o valor B a ser modificado. Se e somente se o valor esperado A e o valor da memória v forem iguais, o valor da memória será modificado para B e retornado verdadeiro, caso contrário, nada será feito e False será retornado. Obviamente, o CAS deve cooperar com a variável volátil, para garantir que a variável obtida cada vez seja o valor mais recente na memória principal. Caso contrário, o antigo valor esperado a sempre será um valor a que não mudará para um thread. Enquanto uma certa operação do CAS falhar, ela nunca terá sucesso.
33. O que é trava otimista e trava pessimista
(1) Bloqueio otimista: assim como o nome, é otimista sobre os problemas de segurança de threads causados por operações simultâneas. O Otimista Lock acredita que a concorrência nem sempre ocorre, por isso não precisa manter o bloqueio e comparará - defina essas duas ações como uma operação atômica para tentar modificar variáveis na memória. Se falhar, significa que ocorre um conflito e, em seguida, deve haver lógica de tentativa correspondente.
(2) Bloqueio pessimista: Assim como o nome, é pessimista sobre os problemas de segurança de threads causados por operações simultâneas. Locks pessimistas acreditam que a concorrência sempre ocorrerá. Portanto, toda vez que um recurso é operado, ele mantém um bloqueio exclusivo, assim como sincronizado, independentemente de estar bloqueado diretamente e o recurso será operado.
34. O que é AQS
Vamos falar brevemente sobre o AQS. O nome completo do AQS é o abstratoqueeds psicronizador. Deve ser um sincronizador abstrato da fila quando traduzido.
Se a base do java.util.Concurrent for CAS, o AQS é o núcleo de todo o pacote de concorrência Java e reentrantlock, CountdownLatch, semáforo, etc. os usam. O AQS realmente conecta toda a entrada na forma de uma fila bidirecional. Por exemplo, reentrantlock. Todos os threads de espera são colocados em uma entrada e conectados em uma fila bidirecional. Se o thread anterior usar o ReentrantLock, a primeira entrada da fila bidirecional começará a ser executada.
O AQS define todas as operações em filas bidirecionais, mas apenas abre os métodos Trylock e TryRerelease para os desenvolvedores usarem. Os desenvolvedores podem reescrever os métodos Trylock e TryRerelease, de acordo com sua própria implementação, para implementar suas próprias funções de simultaneidade.
35. Segurança de thread do modo singleton
Uma questão clichê, a primeira coisa a dizer é que a segurança do thread do padrão de singleton significa que: instâncias de uma determinada classe só serão criadas uma vez em um ambiente multithread. Existem muitas maneiras de escrever o padrão de singleton, deixe -me resumir:
(1) Escrever o padrão de singleton de Hungry Man: Thread Safety
(2) Escrevendo o padrão preguiçoso de singleton: não-thread-segue
(3) Escreva o modo Singleton do bloqueio de verificação dupla: Segurança do thread
36. Qual é a função do semáforo
O semáforo é um semáforo e sua função é limitar o número de simultaneidade em um determinado bloco de código. O semáforo possui um construtor que pode passar int inteiro n, indicando que apenas n threads podem acessar uma determinada peça de código. Se N for excedido, aguarde até que um thread conclua o bloco de código e digite o próximo thread. A partir disso, podemos ver que se o número inteiro n = 1 passou no construtor de semáforo for equivalente a se tornar um sincronizado.
37. Existe apenas uma declaração "contagem de retorno" no método size () de hashtable, então por que você ainda precisa sincronizar?
Isso é uma confusão que eu tinha antes e me pergunto se você pensou sobre essa pergunta. Se houver várias declarações em um método e todas estiverem operando a mesma variável de classe, se você não adicionar bloqueios em um ambiente multithread, isso inevitavelmente causará problemas de segurança de threads. É fácil de entender, mas o método size () claramente tem apenas uma declaração, então por que você ainda precisa adicionar bloqueios?
Em relação a esse problema, eu o entendi trabalhando e estudando lentamente, e há dois motivos principais:
(1) Apenas um encadeamento pode executar o método de sincronização da classe fixa ao mesmo tempo, mas para o método assíncrono da classe, vários threads podem acessá -lo ao mesmo tempo. Então, há um problema. Talvez o Thread A esteja adicionando dados ao executar o método de put de hashtable, e o thread b pode chamar o método size () normalmente para ler o número de elementos atuais na hashtable. O valor lido pode não ser o mais recente. Talvez o Thread A tenha adicionado os dados, mas sem tamanho ++, o Thread B já leu o tamanho. Portanto, para o encadeamento B, o tamanho da leitura deve ser impreciso. Depois de adicionar sincronização ao método size (), significa que o Thread B chama o método Size () somente após o thread a chamadas o método de put, o que garante a segurança do thread
(2) O CPU executa o código, mas não é o código Java. Isso é muito importante e você deve se lembrar disso. O código Java é eventualmente traduzido em código de montagem para execução, e o código de montagem é o código que pode realmente interagir com os circuitos de hardware. Mesmo se você perceber que existe apenas uma linha de código Java e, mesmo se você perceber que o código Java é compilado, existe apenas uma linha de bytecode gerada, isso não significa que, para a camada subjacente, existe apenas uma operação para esta declaração. A frase "contagem de retorno" está assumindo que ela é traduzida em três declarações de montagem para executar, e é inteiramente possível que o encadeamento mude após a execução da primeira frase.
38. Qual fio é o construtor da classe de roscas e o bloco estático chamado por qual fio
Esta é uma pergunta muito complicada e astuta. Lembre -se: o construtor e o bloco estático da classe Thread são chamados pelo thread onde a nova classe de encadeamento está localizada e o código no método de execução é chamado pelo próprio thread.
Se a declaração acima o confundir, deixe -me dar um exemplo, suponha que o novo Thread1 esteja no Thread2 e o novo Thread2 está na função principal, então:
(1) O construtor e o bloco estático do Thread2 são chamados pelo thread principal, e o método Run () de Thread2 é chamado pelo próprio Thread2
(2) O construtor e o bloco estático do Thread1 são chamados pelo Thread2, e o método Run () de Thread1 é chamado pelo próprio Thread1
39. Qual é a melhor escolha entre o método de sincronização e o bloco de sincronização?
Sincronize blocos, o que significa que o código fora do bloco de sincronização é executado de forma assíncrona, o que melhora a eficiência do código mais eficiente do que sincronizar todo o método. Saiba um princípio: a menor faixa de sincronização
Quanto melhor.
Com este artigo, gostaria de mencionar que, embora quanto menor o intervalo de sincronização, melhor, ainda existe um método de otimização chamado de bloqueio nas máquinas virtuais Java, que é aumentar a faixa de sincronização. Isso é útil. Por exemplo, o StringBuffer é uma classe segura para threads. Naturalmente, o método Append () mais usado é um método de sincronização. Quando escrevemos código, anexaremos repetidamente a string, o que significa bloqueio repetidamente -> desbloqueio, o que não é bom para o desempenho, porque isso significa que a máquina virtual Java deve alternar repetidamente entre o estado do kernel e o estado do usuário nesse thread. Portanto, a máquina virtual Java executa uma operação de coarses de bloqueio no código chamado pelo método de múltiplos apêndos, estendendo várias operações de anexos à cabeça e na cauda do método APÊNCIA e transformando-o em um grande bloco de sincronização. Isso reduz o número de bloqueios-> tempos de desbloqueio, melhorando efetivamente a eficiência da execução do código.
40. Como usar pools de threads para empresas com alta concorrência e tempo de execução de tarefas curtas? Como usar pools de threads para empresas com baixa concorrência e tempo de execução de tarefas longas? Como usar pools de threads para empresas com alta concorrência e tempo de execução de serviço longo?
Esta é uma pergunta que vi no site de programação simultânea. Eu fiz essa pergunta por último e espero que todos possam ver e pensar sobre isso, porque essa pergunta é muito boa, muito prática e muito profissional. Em relação a esta questão, minha opinião pessoal é:
(1) Alta concorrência e curto tempo de execução de tarefas, o número de threads do pool de threads pode ser definido como número do núcleo da CPU +1 para reduzir a troca do contexto de rosca
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
Java线程阻塞(Blocked)的类型:
调用sleep函数进入睡眠状态,Thread.sleep(1000)或者TimeUnit.SECONDS.sleep(1),sleep不会释放锁。
等待(wait)某个事件,分为两种,(wait,notify,notifyAll),(await, signal,signalAll) ,后面会详细介绍。wait和await会释放锁,且必须在获取到锁的环境才能调用。
等待锁,synchronized和lock环境中,锁已经被别的线程拿走,等待获取锁。
IO阻塞(Blocked),比如网络等待,文件打开,控制台读取。System.in.read()。