JavaRush /Java Blog /Random-IT /Livello 26. Risposte alle domande dell'intervista sull'ar...
zor07
Livello 31
Санкт-Петербург

Livello 26. Risposte alle domande dell'intervista sull'argomento del livello. Parte 2. Domande 6-9, 11-12

Pubblicato nel gruppo Random-IT
Livello 26. Risposte alle domande dell'intervista sull'argomento del livello.  Parte 2. Domande 6-9, 11-12 - 1

6. Cos'è Kankarensi?

La concorrenza è una libreria di classi in Java che contiene classi speciali ottimizzate per funzionare su più thread. Queste classi sono raccolte in un pacchetto java.util.concurrent. Possono essere divisi schematicamente in base alla funzionalità come segue: Livello 26. Risposte alle domande dell'intervista sull'argomento del livello.  Parte 2. Domande 6-9, 11-12 - 2Raccolte simultanee : un insieme di raccolte che funzionano in modo più efficiente in un ambiente multi-thread rispetto alle raccolte universali standard del java.utilpacchetto. Invece di un wrapper di base Collections.synchronizedListcon blocco dell'accesso all'intera raccolta, vengono utilizzati blocchi sui segmenti di dati oppure il lavoro viene ottimizzato per la lettura parallela dei dati utilizzando algoritmi senza attesa. Code: code non bloccanti e bloccanti con supporto multi-threading. Le code non bloccanti sono progettate per la velocità e il funzionamento senza bloccare i thread. Le code di blocco vengono utilizzate quando è necessario "rallentare" i thread "Producer" o "Consumer" se alcune condizioni non sono soddisfatte, ad esempio, la coda è vuota o traboccata o non esiste un "Consumer" libero. I sincronizzatori sono utilità ausiliarie per la sincronizzazione dei thread. Sono un’arma potente nel calcolo “parallelo”. Esecutori : contiene strutture eccellenti per la creazione di pool di thread, la pianificazione di attività asincrone e l'ottenimento di risultati. Locks - rappresenta meccanismi di sincronizzazione dei thread alternativi e più flessibili rispetto a quelli synchronizeddi waitbase notify. Atomics : classi con supporto per operazioni atomiche su primitive e riferimenti. Fonte:notifyAll

7. Quali lezioni di Kankarensi conosci?

La risposta a questa domanda è perfettamente indicata in questo articolo . Non vedo il motivo di ristamparlo tutto qui, quindi descriverò solo quelle classi con cui ho avuto l'onore di familiarizzare brevemente. ConcurrentHashMap<K, V> - A differenza Hashtabledei blocchi synhronizedsu HashMap, i dati vengono presentati sotto forma di segmenti, divisi in hash di chiavi. Di conseguenza, l'accesso ai dati avviene per segmenti anziché per singolo oggetto. Inoltre, gli iteratori rappresentano i dati per un periodo di tempo specifico e non generano file ConcurrentModificationException. AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray - Cosa succede se in una classe è necessario sincronizzare l'accesso a una semplice variabile di tipo int? È possibile utilizzare i costrutti con synchronizede, quando si utilizzano operazioni atomiche set/get, volatile. Ma puoi fare ancora meglio usando nuove classi Atomic*. Grazie all'utilizzo di CAS, le operazioni con queste classi sono più veloci rispetto a quelle sincronizzate tramite synchronized/volatile. Inoltre, esistono metodi per l'addizione atomica di una determinata quantità, nonché per l'incremento/decremento.

8. Come funziona la classe ConcurrentHashMap?

Al momento della sua introduzione, ConcurrentHashMapgli sviluppatori Java avevano bisogno della seguente implementazione della mappa hash:
  • Sicurezza del filo
  • Nessun blocco sull'intero tavolo durante l'accesso
  • È auspicabile che non vi siano blocchi della tabella durante l'esecuzione di un'operazione di lettura
Le principali idee di implementazione ConcurrentHashMapsono le seguenti:
  1. Elementi della mappa

    A differenza degli elementi HashMap, Entryin ConcurrentHashMapsono dichiarati come volatile. Questa è una caratteristica importante, dovuta anche ai cambiamenti in 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. Funzione hash

    ConcurrentHashMapviene utilizzata anche una funzione di hashing migliorata.

    Lascia che ti ricordi com'era in HashMapJDK 1.2:

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

    Versione da 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);
    }

    Perché è necessario rendere la funzione hash più complessa? Le tabelle in una mappa hash hanno una lunghezza determinata da una potenza di due. Per i codici hash le cui rappresentazioni binarie non differiscono nella posizione bassa e alta, avremo collisioni. Aumentare la complessità della funzione hash risolve semplicemente questo problema, riducendo la probabilità di collisioni nella mappa.

  3. Segmenti

    La mappa è divisa in N segmenti diversi (16 per impostazione predefinita, il valore massimo può essere 16 bit ed è una potenza di due). Ogni segmento è una tabella thread-safe di elementi della mappa. L'aumento del numero di segmenti incoraggerà le operazioni di modifica a estendersi su più segmenti, riducendo la probabilità di blocco in fase di esecuzione.

  4. ConcurrencyLevel

    Questo parametro influisce sull'utilizzo della scheda di memoria e sul numero di segmenti nella scheda.

    Il numero di segmenti verrà scelto come potenza di due più vicina maggiore di concurrencyLevel. Abbassando il concurrencyLevel aumenta la probabilità che i thread blocchino i segmenti della mappa durante la scrittura. La sovrastima dell'indicatore porta a un uso inefficiente della memoria. Se solo un thread modificherà la mappa e il resto la leggerà, si consiglia di utilizzare il valore 1.

  5. Totale

    Quindi, i principali vantaggi e caratteristiche di implementazione ConcurrentHashMap:

    • hashmapLa mappa ha un'interfaccia di interazione simile a
    • Le operazioni di lettura non richiedono blocchi e vengono eseguite in parallelo
    • Spesso le operazioni di scrittura possono essere eseguite anche in parallelo senza blocchi
    • Durante la creazione viene indicato quello richiesto concurrencyLevel, determinato leggendo e scrivendo statistiche
    • Gli elementi della mappa hanno un valore valuedichiarato comevolatile
    Fonte: come funziona ConcurrentHashMap

9. Cos'è la classe Lock?

Per controllare l'accesso ad una risorsa condivisa possiamo utilizzare i lock come alternativa all'operatore sincronizzato. La funzionalità di blocco è inclusa nel pacchetto java.util.concurrent.locks. Innanzitutto, il thread tenta di accedere alla risorsa condivisa. Se è gratuito, viene posizionato un blocco sul thread. Una volta completato il lavoro, il blocco sulla risorsa condivisa viene rilasciato. Se la risorsa non è libera e su di essa è già inserito un blocco, il thread attende finché questo blocco non viene rilasciato. Le classi Lock implementano un'interfaccia Lockche definisce i seguenti metodi:
  • void lock():attende finché non viene acquisito il blocco
  • boolean tryLock():tenta di acquisire un lock; se il lock viene ottenuto, restituisce true . Se il lock non viene acquisito, restituisce false . A differenza del metodo, lock()non attende di acquisire un blocco se non ne è disponibile uno
  • void unlock():rimuove la serratura
  • Condition newCondition():restituisce l'oggetto Conditionassociato al blocco corrente
L'organizzazione del blocco nel caso generale è abbastanza semplice: per ottenere il blocco, viene chiamato il metodo lock()e, dopo aver finito di lavorare con le risorse condivise, viene chiamato il metodo unlock(), che rilascia il blocco. L'oggetto Conditionconsente di gestire il blocco. Di norma, per lavorare con i blocchi, viene utilizzata una classe ReentrantLockdel pacchetto , java.util.concurrent.locks.che implementa l'interfaccia Lock. Diamo un'occhiata all'utilizzo dell'API Java Lock utilizzando un piccolo programma come esempio: quindi, supponiamo di avere una classe Resourcecon un paio di metodi thread-safe e metodi in cui la sicurezza thread non è richiesta.
public class Resource {

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

    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
Ora prendiamo una classe che implementa l'interfaccia Runnablee utilizza i metodi della 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();
    }
}
Ora riscriviamo il programma precedente utilizzando l'API Lock anziché l'API 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();
    }

}
Come puoi vedere dal programma, utilizziamo il metodo tryLock()per assicurarci che il thread attenda solo per un certo periodo di tempo. Se non ottiene un blocco sull'oggetto, registra semplicemente ed esce. Un altro punto importante. È necessario utilizzare un blocco try-finallyper garantire che il blocco venga rilasciato anche se il metodo doSomething()genera un'eccezione. Fonti:

11. Cos'è un mutex?

Un mutex è un oggetto speciale per la sincronizzazione di thread/processi. Possono essere necessari due stati: occupato e libero. Per semplificare, un mutex è una variabile booleana che accetta due valori: busy (true) e free (false). Quando un thread desidera la proprietà esclusiva di un oggetto, contrassegna il suo mutex come occupato e, quando ha finito di lavorare con esso, contrassegna il suo mutex come libero. Un mutex è allegato a ogni oggetto in Java. Solo la macchina Java ha accesso diretto al mutex. È nascosto al programmatore.

12. Cos'è un monitor?

Un monitor è un meccanismo speciale (un pezzo di codice) - un componente aggiuntivo sul mutex, che ne garantisce il corretto funzionamento. Dopotutto, non è sufficiente indicare che l'oggetto è occupato; dobbiamo anche assicurarci che altri thread non tentino di utilizzare l'oggetto occupato. In Java, il monitor viene implementato utilizzando la parola chiave synchronized. Quando scriviamo un blocco sincronizzato, il compilatore Java lo sostituisce con tre pezzi di codice:
  1. All'inizio del blocco synchronizedviene aggiunto il codice che contrassegna il mutex come occupato.
  2. Alla fine del blocco synchronizedviene aggiunto un codice che contrassegna il mutex come libero.
  3. Prima del blocco synchronizedviene aggiunto del codice che controlla se il mutex è occupato, poi il thread deve attendere che venga rilasciato.
Parte 1
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION