JavaRush /Blogue Java /Random-PT /Tradução: as 50 principais perguntas da entrevista por tó...
KapChook
Nível 19
Volga

Tradução: as 50 principais perguntas da entrevista por tópico. Parte 2.

Publicado no grupo Random-PT
A segunda parte da tradução do artigo original As 50 principais perguntas da entrevista sobre Java Thread, respostas para iniciantes e programadores experientes. Primeira parte.
  1. Como verificar se um thread está segurando um bloqueio?

  2. Eu não tinha ideia de que você poderia verificar se um tópico estava bloqueado até que me deparei com essa pergunta em uma entrevista por telefone. java.lang.Thread possui um método holdingLock(), ele retorna verdadeiro se e somente se o thread atual estiver mantendo o monitor em um objeto específico.
  3. Como obter um despejo de thread?

  4. Um dump de thread permite descobrir o que um thread está fazendo no momento. Existem diversas maneiras de obter um dump de thread, dependendo do sistema operacional. No Windows você pode usar a combinação ctrl + Break, no Linux você pode usar o comando kill -3. Você também pode usar o utilitário jstack; ele opera no ID do processo, que você pode descobrir usando outro utilitário jps.
  5. Qual parâmetro JVM é usado para controlar o tamanho da pilha do thread?

  6. Este é um dos parâmetros -Xss simples usados ​​​​para controlar o tamanho da pilha de um thread em Java.
  7. Diferenças entre sincronizado e ReentrantLock?

  8. Houve momentos em que a única maneira de conseguir a exclusão mútua era através da palavra-chave sincronizada, mas ela tinha várias desvantagens, como não ser capaz de estender o bloqueio além de um método ou bloco de código, etc. Java 5 resolve esse problema fornecendo controle mais granular por meio da interface Lock. ReentrantLock é uma implementação comum de Lock que fornece um Lock com o mesmo comportamento e semântica básicos de um monitor implícito, obtido usando métodos sincronizados, mas com recursos aprimorados.
  9. Dados 3 threads T1, T2 e T3? Como implementar a sequência T1, T2, T3?

  10. A consistência pode ser alcançada de várias maneiras, mas você pode simplesmente usar o método join() para iniciar um thread quando outro terminar de ser executado. Para implementar a sequência fornecida, você precisa iniciar o último thread primeiro e, em seguida, chamar o método join() na ordem inversa, ou seja, T3 chama T2.join e T2 chama T1.join, então T1 terminará primeiro e T3 por último .
  11. O que o método de rendimento faz?

  12. O método yield é uma maneira de pedir a um thread que desista da CPU para que outro thread possa ser executado. Este é um método estático e apenas garante que o thread atual desista do processador, mas não decide para qual thread a execução irá.
  13. Qual é o nível de simultaneidade do ConcurrentHashMap?

  14. ConcurrentHashMap atinge sua escalabilidade e segurança de thread dividindo o mapa real em seções. Essa separação é alcançada usando um nível de paralelismo. Este é um parâmetro opcional para o construtor ConcurrentHashMap e seu valor padrão é 16.
  15. O que é semáforo?

  16. Semáforo é um novo tipo de sincronizador. Este é um semáforo com contador. Conceitualmente, um semáforo controla um conjunto de permissões. Cada aquisição() bloqueia, se necessário, antes que a permissão esteja disponível e, em seguida, a adquire. Cada release() adiciona uma permissão, potencialmente liberando o adquirente bloqueador. Entretanto, isso não usa objetos de permissão reais; O semáforo simplesmente armazena o número de disponíveis e age de acordo. O semáforo é usado para proteger recursos caros que estão disponíveis em quantidades limitadas, como uma conexão com um banco de dados em pool.
  17. O que acontece se a fila do pool de threads já estiver cheia e você enviar uma tarefa?

  18. Se a fila do pool de threads estiver cheia, a tarefa enviada será "rejeitada". O método submit() do ThreadPoolExecutor lança uma RejectedExecutionException, após a qual o RejectedExecutionHandler é chamado.
  19. Diferenças entre os métodos submit() e execute() em um pool de threads?

  20. Ambos os métodos são formas de enviar uma tarefa para um pool de threads, mas há uma pequena diferença entre eles. Execute (comando Runnable) é definido na interface do Executor e executa a tarefa fornecida no futuro, mas o mais importante, não retorna nada. Por outro lado, submit() é um método sobrecarregado, pode aceitar tarefas Runnable e Callable e pode retornar um objeto Future que pode ser usado para cancelar a execução e/ou aguardar o resultado de um cálculo. Este método é definido na interface ExecutorService, que herda da interface Executor, e cada classe do pool de threads, como ThreadPoolExecutor ou ScheduledThreadPoolExecutor, herda esses métodos.
  21. O que é um método de bloqueio?

  22. Um método de bloqueio é um método que bloqueia até que a tarefa seja concluída, por exemplo, o método ServerSocket accept() bloqueia enquanto espera a conexão do cliente. Aqui, bloqueio significa que o controle não retornará ao método de chamada até que o trabalho seja concluído. Por outro lado, existem métodos assíncronos ou sem bloqueio que são concluídos antes da conclusão da tarefa.
  23. O thread Swing é seguro?

  24. Simplificando, não, o Swing não é seguro para threads, mas você precisa explicar o que quer dizer com isso, mesmo que o entrevistador não pergunte. Quando dizemos que Swing não é thread-safe, geralmente nos referimos ao fato de que é um componente que não pode ser modificado por vários threads. Todas as alterações nos componentes da GUI devem ser feitas no thread AWT, e o Swing fornece métodos síncronos e assíncronos para agendar tais alterações.
  25. Diferenças entre invocarAndWait e invocarLater?

  26. Esses são dois métodos da API Swing que permitem aos desenvolvedores atualizar componentes da GUI a partir de threads em vez de a partir do thread do Event Manager. InvokeAndWait() atualiza de forma síncrona um componente GUI, como uma barra de progresso; cada vez que o progresso é feito, a barra deve ser atualizada para refletir as alterações. Se o progresso estiver sendo rastreado em outro thread, ele deverá chamar activateAndWait() para atribuir o thread do Event Dispatcher para atualizar esse componente. E invocaLater() é uma chamada assíncrona para atualizar componentes.
  27. Quais métodos da API Swing são thread-safe?

  28. Esta questão é novamente sobre Swing e segurança de thread, embora os componentes Swing não sejam thread-safe, existem métodos que podem ser chamados com segurança a partir de vários threads. Eu sei que repaint() e revalidate() são thread-safe, mas existem outros métodos em diferentes componentes do Swing, como os métodos setText() do JTextComponent e insert() e append() do JTextArea.
  29. Como criar objetos imutáveis?

  30. Esta questão pode parecer não ter nada a ver com multithreading e simultaneidade, mas tem. A imutabilidade ajuda a simplificar códigos paralelos já complexos. Um objeto imutável é muito caro para os desenvolvedores porque pode ser propagado sem qualquer sincronização. Infelizmente, Java não possui a anotação @Immutable, o que tornará seu objeto imutável, para isso os desenvolvedores precisam trabalhar muito. Para criar um objeto imutável, você precisa seguir o básico: inicialização no construtor, sem setters, sem vazamento de referência, armazenamento de cópias separadas de objetos mutáveis.
  31. O que é ReadWriteLock?

  32. Em geral, ReadWriteLock é o resultado de uma técnica de análise de bloqueio para melhorar o desempenho de aplicações paralelas. Esta é uma interface que foi adicionada no Java 5. Ela opera em um par de bloqueios relacionados, um para operações de leitura e outro para operações de gravação. Um bloqueio de leitor pode ser mantido simultaneamente por vários threads de leitura até que não haja mais escritores. O bloqueio de escrita é exclusivo. Se desejar, você pode implementar uma interface com seu conjunto de regras ou usar ReentrantReadWriteLock, que suporta no máximo 65.535 bloqueios de gravação recursivos e 65.535 bloqueios de leitura.
  33. O que é giro ocupado?

  34. Busy Spin é uma técnica que os programadores usam para forçar um thread a esperar sob uma determinada condição. Ao contrário dos métodos tradicionais, wait(), sleep() ou yield(), que envolvem a cessão do controle do processador, este método não cede o processador; em vez disso, ele simplesmente executa um loop vazio. Por que alguém faria isso? Para salvar o cache do processador. Em sistemas multi-core, é possível que um thread suspenso continue em execução em outro núcleo, o que significa uma reconstrução do cache. Para evitar reconstruções dispendiosas, o programador prefere esperar menos usando o giro ocupado.
  35. Diferenças entre variáveis ​​voláteis e atômicas?

  36. Esta é uma questão bastante interessante, a princípio as variáveis ​​voláteis e atômicas parecem muito semelhantes, mas ainda são diferentes. Uma variável volátil fornece uma garantia de que uma gravação ocorrerá antes de qualquer gravação subsequente; ela não garante atomicidade. Por exemplo, a operação count++ não se tornará atômica simplesmente porque count é declarada volátil. Por outro lado, a classe AtomicInteger fornece um método atômico para realizar tais operações complexas atomicamente, por exemplo getAndIncrement() é uma substituição atômica para o operador de incremento, pode ser usado para incrementar atomicamente o valor atual em um. Existem também versões atômicas para outros tipos de dados.
  37. O que acontece se um thread lançar uma exceção em um bloco sincronizado?

  38. Esta é outra pergunta capciosa para programadores Java regulares. Não importa como você sai de um bloco sincronizado, normalmente finalizando a execução ou repentinamente lançando uma exceção, o thread libera o bloqueio adquirido quando entra no bloco sincronizado. Esta é uma das razões pelas quais prefiro um bloco de bloqueio sincronizado a uma interface que requer cuidado especial ao liberar o bloqueio, geralmente conseguido liberando o bloqueio em um bloco final.
  39. O que é o bloqueio duplo verificado do Singleton?

  40. Esta é uma das perguntas de entrevista mais populares e, ainda assim, apesar de sua popularidade, as chances de um candidato respondê-la são de 50%, na melhor das hipóteses. Metade das vezes eles falham em escrever o código e na outra metade em explicar como ele foi quebrado e corrigido no Java 1.5. Esta é uma maneira antiga de criar um singleton thread-safe que tenta otimizar o desempenho bloqueando apenas quando a instância do singleton é instanciada pela primeira vez, mas devido à complexidade e ao fato de ter sido quebrado no JDK 1.4, eu pessoalmente não gosto isto. Ainda assim, mesmo que você não prefira essa abordagem, é útil conhecê-la do ponto de vista da entrevista.
  41. Como criar um Singleton thread-safe?

  42. Esta questão complementa a anterior. Se você disser que não gosta de bloqueio verificado duas vezes, o entrevistador será forçado a perguntar sobre maneiras alternativas de criar um Singleton seguro para threads. E são, você pode aproveitar as vantagens dos recursos de carregamento de classe e inicialização de variável estática para instanciar o Singleton, ou pode aproveitar as vantagens do poderoso tipo enum.
  43. Liste 3 costumes que você segue na programação paralela?

  44. Esta é minha pergunta favorita porque acredito que existem certas regras que precisam ser seguidas ao escrever código paralelo, o que ajuda no desempenho, depuração e suporte. Abaixo estão as 3 melhores regras que acredito que todo programador Java deve seguir:
    • Sempre dê nomes significativos aos seus tópicos
    • Encontrar um bug ou rastrear uma exceção em código paralelo é uma tarefa bastante difícil. OrderProcessor, QuoteProcessor ou TradeProcessor é muito melhor que Thread-1. Fio-2 e Fio-3. O nome deve refletir a tarefa executada pelo thread. Todos os principais frameworks e até mesmo o JDK seguem esta regra.
    • Evite bloquear ou reduzir o escopo da sincronização
    • O bloqueio é caro e a troca de contexto é ainda mais cara. Tente evitar ao máximo a sincronização e o bloqueio e você reduzirá a seção crítica ao mínimo necessário. É por isso que prefiro um bloqueio cronometrado a um método cronometrado, porque oferece controle absoluto sobre a extensão do bloqueio.
    • Entre sincronizadores e esperar e notificar, escolha sincronizadores
    • Em primeiro lugar, sincronizadores como CountDownLatch, Semaphore, CyclicBarrier ou Exchanger simplificam a codificação. É muito difícil implementar um fluxo de controle complexo usando espera e notificação. Segundo, essas classes são escritas e mantidas pelos melhores do ramo e há uma boa chance de que sejam otimizadas ou substituídas por código melhor em versões futuras do JDK. Ao usar utilitários de sincronização de alto nível, você obtém automaticamente todos esses benefícios.
    • Entre Coleção Simultânea e Coleção Sincronizada, escolha Coleção Simultânea
    • Esta é outra regra simples, fácil de seguir e colher os benefícios. As coleções simultâneas são mais escaláveis ​​do que suas contrapartes sincronizadas, por isso é melhor usá-las ao escrever código paralelo. Então, da próxima vez que você precisar de um mapa, pense em ConcurrentHashMap antes de pensar em Hashtable.
  45. Como forçar o início de um thread?

  46. Esta é uma questão de como forçar a execução da coleta de lixo. Resumindo, de jeito nenhum, é claro que você pode fazer uma consulta usando System.gc(), mas isso não garante nada. Não há absolutamente nenhuma maneira de forçar o início de um thread em Java, isso é controlado pelo agendador de threads e Java não fornece nenhuma API para controlá-lo. Esta parte do Java ainda é aleatória.
  47. Qual é a estrutura Fork/Join?

  48. A estrutura Fork/Join introduzida no JDK 7 é um utilitário poderoso que permite ao desenvolvedor aproveitar as vantagens dos múltiplos processadores dos servidores modernos. Ele foi projetado para trabalhos que podem ser divididos recursivamente em pequenas partículas. O objetivo é usar todo o poder computacional disponível para aumentar o desempenho da sua aplicação. Uma vantagem significativa desta estrutura é que ela usa um algoritmo de roubo de trabalho (de trabalho - trabalhar e roubar - para roubar). Threads de trabalho que ficaram sem trabalhos podem roubar trabalhos de outros threads que ainda estão ocupados.
  49. Qual é a diferença entre chamadas para wait() e sleep()?

  50. Embora wait e sleep representem uma espécie de pausa em uma aplicação Java, eles são dispositivos para necessidades diferentes. Wait é usado para comunicação de thread interno, ele cede ao bloqueio se a condição de espera for verdadeira e aguarda notificação se as ações de outro thread tornarem a condição de espera falsa. Por outro lado, o método sleep() simplesmente desiste do processador ou interrompe a execução do thread atual por um período de tempo especificado. Chamar sleep() não libera o bloqueio mantido pelo thread atual.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION