JavaRush /Blogue Java /Random-PT /Nível 26. Respostas às perguntas da entrevista sobre o te...
zor07
Nível 31
Санкт-Петербург

Nível 26. Respostas às perguntas da entrevista sobre o tema do nível. Parte 2. Perguntas 6-9, 11-12

Publicado no grupo Random-PT
Nível 26. Respostas às perguntas da entrevista sobre o tema do nível.  Parte 2. Questões 6-9, 11-12 - 1

6. O que é Cancarenzi?

Simultaneidade é uma biblioteca de classes em Java que contém classes especiais otimizadas para trabalhar em vários threads. Essas classes são coletadas em um pacote java.util.concurrent. Eles podem ser divididos esquematicamente de acordo com a funcionalidade da seguinte forma: Nível 26. Respostas às perguntas da entrevista sobre o tema do nível.  Parte 2. Perguntas 6-9, 11-12 - 2Coleções Simultâneas - um conjunto de coleções que funcionam de forma mais eficiente em um ambiente multithread do que as coleções universais padrão do java.utilpacote. Em vez de um wrapper básico Collections.synchronizedListcom bloqueio de acesso a toda a coleção, são usados ​​​​bloqueios em segmentos de dados ou o trabalho é otimizado para leitura paralela de dados usando algoritmos sem espera. Filas - filas sem bloqueio e bloqueio com suporte multi-threading. As filas sem bloqueio são projetadas para velocidade e operação sem bloquear threads. As filas de bloqueio são usadas quando você precisa “desacelerar” os threads “Produtor” ou “Consumidor” se algumas condições não forem atendidas, por exemplo, a fila está vazia ou transbordada, ou não há “Consumidor” livre. Sincronizadores são utilitários auxiliares para sincronização de threads. Eles são uma arma poderosa na computação “paralela”. Executores – contém excelentes frameworks para criação de pools de threads, agendamento de tarefas assíncronas e obtenção de resultados. Locks - representa mecanismos de sincronização de threads alternativos e mais flexíveis em comparação aos synchronizedbásicos . Atomics - classes com suporte para operações atômicas em primitivas e referências. Fonte:waitnotifynotifyAll

7. Quais aulas de Kankarensi você conhece?

A resposta a esta pergunta está perfeitamente exposta neste artigo . Não vejo sentido em reimprimir tudo aqui, então darei descrições apenas das classes com as quais tive a honra de me familiarizar brevemente. ConcurrentHashMap<K, V> - Ao contrário Hashtabledos blocos e synhronizedem HashMap, os dados são apresentados na forma de segmentos, divididos em hashes de chaves. Como resultado, os dados são acessados ​​por segmentos e não por um único objeto. Além disso, os iteradores representam dados de um período de tempo específico e não lançam ConcurrentModificationException. AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray - E se em uma classe você precisar sincronizar o acesso a uma variável simples do tipo int? Você pode usar construções com synchronizede, ao usar operações atômicas set/get, volatile. Mas você pode fazer ainda melhor usando novas classes Atomic*. Devido ao uso do CAS, as operações com essas classes são mais rápidas do que se sincronizadas via synchronized/volatile. Além disso, existem métodos para adição atômica por um determinado valor, bem como incremento/decremento.

8. Como funciona a classe ConcurrentHashMap?

No momento de sua introdução, ConcurrentHashMapos desenvolvedores Java precisavam da seguinte implementação de mapa hash:
  • Segurança do fio
  • Sem bloqueios em toda a tabela ao acessá-la
  • É desejável que não haja bloqueios de tabela ao realizar uma operação de leitura
As principais ideias de implementação ConcurrentHashMapsão as seguintes:
  1. Elementos do mapa

    Ao contrário dos elementos HashMap, Entryin ConcurrentHashMapsão declarados como volatile. Esta é uma característica importante, também devido às alterações no JMM .

    static final class HashEntry<K, V> {
        final K key;
        final int hash;
        volatile V value;
        final HashEntry<K, V> next;
    
        HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
            this .key = key;
            this .hash = hash;
            this .next = next;
            this .value = value;
         }
    
        @SuppressWarnings("unchecked")
        static final <K, V> HashEntry<K, V>[] newArray(int i) {
            return new HashEntry[i];
        }
    }
  2. Função hash

    ConcurrentHashMapuma função de hash aprimorada também é usada.

    Deixe-me lembrá-lo de como era no HashMapJDK 1.2:

    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    Versão do ConcurrentHashMap JDK 1.5:

    private static int hash(int h) {
        h += (h << 15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h << 3);
        h ^= (h >>> 6);
        h += (h << 2) + (h << 14);
        return h ^ (h >>> 16);
    }

    Por que é necessário tornar a função hash mais complexa? As tabelas em um mapa hash têm comprimento determinado por uma potência de dois. Para códigos hash cujas representações binárias não diferem em posição baixa e alta, teremos colisões. Aumentar a complexidade da função hash apenas resolve esse problema, reduzindo a probabilidade de colisões no mapa.

  3. Segmentos

    O mapa é dividido em N segmentos diferentes (16 por padrão, o valor máximo pode ser 16 bits e é uma potência de dois). Cada segmento é uma tabela thread-safe de elementos do mapa. Aumentar o número de segmentos incentivará as operações de modificação a abranger vários segmentos, reduzindo a probabilidade de bloqueio em tempo de execução.

  4. Nível de simultaneidade

    Este parâmetro afeta o uso do cartão de memória e o número de segmentos no cartão.

    O número de segmentos será escolhido como a potência mais próxima de dois maior que concurrencyLevel. A redução do concurrencyLevel aumenta a probabilidade de os threads bloquearem os segmentos do mapa durante a gravação. A superestimação do indicador leva ao uso ineficiente da memória. Se apenas um thread modificar o mapa e o restante for lido, é recomendado usar o valor 1.

  5. Total

    Então, as principais vantagens e recursos de implementação ConcurrentHashMap:

    • hashmapO mapa possui uma interface de interação semelhante a
    • As operações de leitura não requerem bloqueios e são executadas em paralelo
    • Muitas vezes, as operações de gravação também podem ser executadas em paralelo sem bloqueio
    • Ao criar, é indicado o necessário concurrencyLevel, determinado pela leitura e gravação de estatísticas
    • Os elementos do mapa têm um valor valuedeclarado comovolatile
    Fonte: Como funciona o ConcurrentHashMap

9. O que é a classe Lock?

Para controlar o acesso a um recurso compartilhado, podemos usar bloqueios como alternativa ao operador sincronizado. A funcionalidade de bloqueio é empacotada em java.util.concurrent.locks. Primeiro, o thread tenta acessar o recurso compartilhado. Se estiver livre, um cadeado será colocado no fio. Assim que o trabalho for concluído, o bloqueio do recurso compartilhado é liberado. Se o recurso não estiver livre e um bloqueio já estiver colocado nele, o thread aguardará até que esse bloqueio seja liberado. As classes Lock implementam uma interface Lockque define os seguintes métodos:
  • void lock():espera até que o bloqueio seja adquirido
  • boolean tryLock():tenta adquirir um bloqueio; se o bloqueio for obtido, ele retorna true . Se o bloqueio não for adquirido, ele retornará false . Ao contrário do método, lock()ele não espera para adquirir um bloqueio se este não estiver disponível
  • void unlock():remove o bloqueio
  • Condition newCondition():retorna o objeto Conditionque está associado ao bloqueio atual
A organização do bloqueio no caso geral é bastante simples: para obter o bloqueio chama-se o método lock(), e após terminar de trabalhar com os recursos compartilhados chama-se o método unlock(), que libera o bloqueio. O objeto Conditionpermite gerenciar o bloqueio. Via de regra, para trabalhar com bloqueios, é utilizada uma classe ReentrantLockdo pacote , java.util.concurrent.locks.que implementa a interface Lock. Vejamos como usar a API Java Lock usando um pequeno programa como exemplo: Então, digamos que temos uma classe Resourcecom alguns métodos thread-safe e métodos onde a segurança de thread não é necessária.
public class Resource {

    public void doSomething(){
        // пусть здесь происходит работа с базой данных
    }

    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
Agora vamos pegar uma classe que implementa a interface Runnablee usa métodos de classe Resource.
public class SynchronizedLockExample implements Runnable{

    // экземпляр класса Resource для работы с методами
    private Resource resource;

    public SynchronizedLockExample(Resource r){
        this.resource = r;
    }

    @Override
    public void run() {
        synchronized (resource) {
            resource.doSomething();
        }
        resource.doLogging();
    }
}
Agora vamos reescrever o programa acima usando a API Lock em vez do synchronized.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{

    private Resource resource;
    private Lock lock;

    public ConcurrencyLockExample(Resource r){
        this.resource = r;
        this.lock = new ReentrantLock();
    }

    @Override
    public void run() {
        try {
            // лочим на 10 секунд
            if(lock.tryLock(10, TimeUnit.SECONDS)){
            resource.doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //убираем лок
            lock.unlock();
        }
        // Для логгирования не требуется потокобезопасность
        resource.doLogging();
    }

}
Como você pode ver no programa, usamos o método tryLock()para garantir que o thread espere apenas um determinado período de tempo. Se não obtiver um bloqueio no objeto, ele simplesmente efetua login e sai. Outro ponto importante. Você deve usar um bloco try-finallypara garantir que o bloqueio será liberado mesmo se o método doSomething()lançar uma exceção. Fontes:

11. O que é um mutex?

Um mutex é um objeto especial para sincronizar threads/processos. Pode levar dois estados - ocupado e livre. Para simplificar, um mutex é uma variável booleana que assume dois valores: ocupado (verdadeiro) e livre (falso). Quando um thread deseja propriedade exclusiva de um objeto, ele marca seu mutex como ocupado e, quando termina de trabalhar com ele, marca seu mutex como livre. Um mutex é anexado a cada objeto em Java. Somente a máquina Java tem acesso direto ao mutex. Está escondido do programador.

12. O que é um monitor?

Um monitor é um mecanismo especial (um pedaço de código) - um complemento sobre o mutex, que garante o correto funcionamento dele. Afinal, não basta marcar que o objeto está ocupado; devemos também garantir que outras threads não tentem utilizar o objeto ocupado. Em Java, o monitor é implementado usando a palavra-chave synchronized. Quando escrevemos um bloco sincronizado, o compilador Java o substitui por três trechos de código:
  1. No início do bloco synchronized, é adicionado um código que marca o mutex como ocupado.
  2. No final do bloco, synchronizedé adicionado um código que marca o mutex como livre.
  3. Antes do bloco synchronizedé adicionado um código que verifica se o mutex está ocupado, então a thread deve aguardar sua liberação.
Parte 1
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION