Questa è una mappa mentale su JUC, inclusa la comunicazione tra thread, Riepilogo delle conoscenze su classi contenitore simultanee, interfacce Callable, code di blocco, pool di thread ThreadPool, ecc.
Modificato alle 2023-12-20 18:03:44個人求職簡歷模板的暗黑配色方案,包括個人簡介、職業規劃、行業經驗、自我評價等多個部分,讓你的簡歷更出彩。使用模板可以極大地提高效率,用戶不需要從頭開始設計結構和內容,只需在模板的基礎上填寫或添加自己的信息即可,這樣可以節省大量的時間和精力,歡迎參考使用!持續分享給大家……
Se non sai come scrivere un articolo, sarai nei guai come manager dei sistemi informativi. Una guida passo passo su come scrivere un documento sulla gestione del rischio. Se ne hai bisogno, ritiralo velocemente!
Il programma dietetico formula un programma dietetico scientifico e ragionevole per soddisfare i nutrienti e l'energia richiesti dal corpo, mantenendo così una buona salute e una buona postura.
個人求職簡歷模板的暗黑配色方案,包括個人簡介、職業規劃、行業經驗、自我評價等多個部分,讓你的簡歷更出彩。使用模板可以極大地提高效率,用戶不需要從頭開始設計結構和內容,只需在模板的基礎上填寫或添加自己的信息即可,這樣可以節省大量的時間和精力,歡迎參考使用!持續分享給大家……
Se non sai come scrivere un articolo, sarai nei guai come manager dei sistemi informativi. Una guida passo passo su come scrivere un documento sulla gestione del rischio. Se ne hai bisogno, ritiralo velocemente!
Il programma dietetico formula un programma dietetico scientifico e ragionevole per soddisfare i nutrienti e l'energia richiesti dal corpo, mantenendo così una buona salute e una buona postura.
JUC
sincronizzato
bloccare l'oggetto
Metodi di sincronizzazione comuni
Oggetto Lock: oggetto chiamata al metodo
Metodo di sincronizzazione statica
Oggetto Lock: coppia di bytecode della classe corrente
blocco del metodo sincronizzato
Oggetto lock: oggetto bytecode
Oggetto Lock: oggetto della classe di implementazione
Controllato da JVM
sincronizzata è anche una serratura rientrante
Quando lo stesso thread acquisisce il blocco nel metodo esterno, il metodo interno che entra nel thread acquisirà automaticamente il blocco. ReentrantLock e sincronizzato in Java sono entrambi blocchi rientranti Un vantaggio dei blocchi rientranti è che possono evitare i deadlock in una certa misura.
Serratura
ReentrantLock serratura rientrante
new ReentrantLock() per impostazione predefinita utilizza il blocco ingiusto
new ReentrantLock (true) Il passaggio in true è un lock corretto
Blocco giusto e blocco ingiusto
Fair lock: il thread all'inizio della coda di attesa ottiene per primo il lock.
Blocco ingiusto: un thread casuale ottiene il blocco, causando il problema della fame del thread
A causa di blocchi non corretti, un determinato thread non può essere eseguito in modo coerente e altri thread catturano il blocco ogni volta.
Attendi un tempo limitato
tryLock booleano pubblico (timeout lungo, unità TimeUnit)
Dopo aver atteso per un periodo di tempo, prova ad acquisire il lock. Se non viene acquisito, restituisce false e, se viene acquisito, restituisce true (l'attesa continua per acquisire il lock bloccherà il thread finché il lock non verrà acquisito e. il tempo di attesa è scaduto)
La differenza tra ReentrantLock e sincronizzato
Stesso
sincronizzato e ReentrantLock sono entrambi blocchi esclusivi
sincronizzato e ReentrantLock sono entrambi blocchi rientranti
sincronizzato e ReentrantLock sono entrambi blocchi pessimistici
diverso
Il blocco e lo sblocco sincronizzati vengono completati da jvm e non possono essere controllati dall'utente, mentre il blocco e lo sblocco di ReentrantLock sono controllati dall'utente.
ReentrantLock è un blocco rientrante È necessario rilasciare manualmente il blocco più volte dopo averlo bloccato, ma la sincronizzazione viene completata automaticamente.
sincronizzato non può rispondere agli interrupt. Aspetterà finché non riuscirà a ottenere il blocco. Tuttavia, ReentrantLock può utilizzare tryLock per rispondere agli interruzioni. Se non riesce a ottenere il blocco per un periodo di tempo, non lo richiederà blocca e aspetta.
Blocco di lettura-scrittura ReentrantReadWriteLock
rientrantReadWriteLock.writeLock() acquisisce il blocco di lettura
rientrantReadWriteLock.readLock() acquisisce il blocco in scrittura
Un blocco di lettura-scrittura consente a più thread di lettura di accedervi contemporaneamente, ma quando un thread di scrittura vi accede, tutti i thread di lettura e gli altri thread di scrittura verranno bloccati.
La scrittura non può essere eseguita contemporaneamente
Lettura e scrittura non sono simultanee
La lettura può essere effettuata contemporaneamente
Blocca il downgrade
Il downgrade del blocco significa il downgrade da un blocco di scrittura a un blocco di lettura. Quando il thread corrente possiede il blocco di scrittura, il processo di acquisizione di nuovo del blocco di lettura e successivamente di rilascio del blocco di scrittura è il downgrade del blocco.
Comunicazione tra thread
sincronizzato
Aspettare
blocca oggetto .wait ()
Rilascia il blocco, trasferisci i diritti di esecuzione della CPU, attendi di essere risvegliato per riacquisire il blocco, quindi esegui il codice in attesa.
veglia
bloccare l'oggetto. notificare ( )
Riattiva casualmente un thread in attesa, anche il thread risvegliato deve competere per il blocco
Blocca oggetto. notificaTutto ( )
Riattiva tutti i thread in attesa
ReentrantLock
Aspettare
rientroLock . nuovaCondizione ( ) attendono ( )
È possibile creare più condizioni di blocco
Condizione aCondition =reentrantLock.newCondition();
Condizione bCondition =reentrantLock.newCondition();
Condizione cCondition =reentrantLock.newCondition();
veglia
condizione.
Riattiva un thread in attesa di riattivarsi per l'oggetto condizione di blocco specificato
condizione.segnaleTutto ( )
Riattiva tutti i thread in attesa dell'oggetto condizione di blocco specificato.
Più dettagliato che sincronizzato, puoi specificare un thread in attesa da riattivare
falsa eccitazione
La condizione if del thread risvegliato non è più soddisfatta, ma poiché è risvegliato, verrà risvegliato in attesa dopo aver acquisito la risorsa di blocco, causando la confusione dell'ordine di esecuzione del thread.
La soluzione è sostituire la condizione if con where. Dopo essere stati risvegliati e aver afferrato la serratura, la condizione deve ancora essere giudicata.
Ciò creerà uno stato di animazione sospesa in cui tutti i thread sono in attesa.
Soluzione: utilizzare notifyAll o signalAll per riattivare tutti i thread che non soddisfano le condizioni entreranno comunque nello stato di attesa.
Classe contenitore simultanea
Classe dello strumento Raccolte
lista sincronizzata
mappa sincronizzata
raccolta sincronizzata
sincronizzatoSet
mappa ordinata sincronizzata
sincronizzatoSortedSet
Converti contenitori thread-safe in contenitori thread-safe inclusi elenco, set e mappa
Copia su scrittura
Contenitori copia su scrittura
Quando aggiungiamo elementi a un contenitore, non li aggiungiamo direttamente al contenitore corrente. Invece, copiamo prima il contenitore corrente e creiamo un nuovo contenitore. Dopo aver aggiunto gli elementi, copiamo il file contenitore originale. Il riferimento punta al nuovo contenitore
Il contenitore CopyOnWrite è anche un'idea di separazione tra lettura e scrittura. Lettura e scrittura sono contenitori diversi.
Leggi contemporaneamente il contenitore CopyOnWrite senza bloccarlo e bloccalo durante la scrittura
Classe ausiliaria
CountDownLatch (contatore conto alla rovescia)
new CountDownLatch(int count) crea un'istanza di un contatore con un valore iniziale di count
Ogni volta che viene chiamato countDown(), il contatore diminuisce di uno.
wait() attende l'esecuzione quando il contatore scende a 0
Barriera ciclica (Barriera ciclica)
CyclicBarrier(int Parties, Runnable BarrierAction) crea un'istanza CyclicBarrier Party specifica il numero di thread che partecipano in attesa l'uno dell'altro. BarrierAction è un comando Runnable opzionale che viene eseguito solo una volta in ogni punto barriera e può condividere lo stato prima di eseguire attività successive. Questa operazione viene eseguita dall'ultimo thread che entra nel punto barriera
CyclicBarrier(int parti) crea un'istanza CyclicBarrier, le parti specificano il numero di thread che partecipano in attesa l'uno dell'altro.
Quando viene chiamato il metodo wait(), indica che il thread corrente ha raggiunto il punto barriera. Il thread corrente è bloccato ed entra nello stato di sospensione. Il thread corrente non verrà risvegliato finché tutti i thread non raggiungono il punto barriera.
Semaforo (semaforo)
Il semaforo può controllare il numero di thread che accedono contemporaneamente. Supponendo che il numero di risorse sia N, ogni thread può ottenere una risorsa. Tuttavia, quando la risorsa viene allocata, i thread successivi devono bloccarsi e attendere fino a quando il thread che la conteneva in precedenza la risorsa rilascia la risorsa continua.
public Semaforo(int permessi) //Metodo di costruzione, i permessi si riferiscono al numero di risorse (semaforo)
public void acquire() lancia InterruptedException // Occupa risorse Quando un thread chiama l'operazione di acquisizione, acquisisce con successo il semaforo (il semaforo viene decrementato di 1) oppure attende finché un thread non rilascia il semaforo o va in timeout.
public void release() // (release) aumenterà effettivamente il valore del semaforo di 1, quindi riattiverà il thread in attesa
Interfaccia richiamabile
utilizzo
Crea un'interfaccia richiamabile basata su classi e sovrascrivi il metodo di chiamata
Crea un FutureTask, chiama il costruttore con parametri e passa l'oggetto della classe di implementazione richiamabile
nuovo FutureTask<String>(chiamabile)
Crea un oggetto classe thread new Thread(futureTask).start() e chiama il metodo start
FutureTask.get() ottiene le informazioni sulla restituzione del thread
Precauzioni per l'uso
FutureTask.get() bloccherà il thread corrente. Si consiglia di inserirlo alla fine.
Calcola solo una volta, FutureTask riutilizzerà i risultati calcolati in precedenza.
La differenza tra interfaccia richiamabile e interfaccia eseguibile
I metodi specifici sono diversi, uno è il metodo call e l'altro è il metodo run.
Runnable non ha valore restituito, ma callable può ottenere l'oggetto valore restituito
Il metodo run non può generare eccezioni e può solo utilizzare eccezioni all'interno del metodo run, mentre il metodo call può generare eccezioni.
Quattro modi per ottenere il multithreading
Eredita il thread, riscrivi il metodo run e chiama start per eseguire il thread
Implementa l'interfaccia Runnable, riscrivi il metodo run e chiama il thread di esecuzione iniziale
Implementa l'interfaccia Callable, riscrivi il metodo di chiamata, crea un oggetto FutureTask ed esegui il metodo di avvio di Thread per eseguire il thread.
Crea un pool di thread ed esegui il metodo di invio
coda di blocco
BlockingQueue è una coda di blocco
Classe di implementazione
ArrayBlockingQueue: una coda di blocco delimitata composta da una struttura di array
LinkedBlockingQueue: una coda di blocco delimitata (ma la dimensione predefinita è integer.MAX_VALUE) composta da una struttura di elenco collegato
PriorityBlockingQueue: coda di blocco illimitato che supporta l'ordinamento per priorità
DelayQueue: coda di blocco illimitato ritardato implementata utilizzando la coda di priorità
SynchronousQueue: una coda bloccante che non memorizza elementi, ovvero una coda con un singolo elemento
LinkedTransferQueue: coda di blocco illimitata composta da un elenco collegato
LinkedBlockingDeque: una coda di blocco bidirezionale composta da un elenco collegato
Quattro gruppi di metodi
lanciare un'eccezione
inserire
aggiungere (e)
Quando la coda di blocco è piena, l'aggiunta e l'inserimento di elementi nella coda genererà IllegalStateException:Queue full
Rimuovere
rimuovere()
Quando la coda di blocco è vuota, la rimozione di elementi dalla coda genererà NoSuchElementException.
esaminare
elemento()
Quando la coda di blocco è vuota, la chiamata dell'elemento per controllare l'elemento genererà NoSuchElementException.
valore speciale
inserire
offerta(e)
Metodo di inserimento, successo vero, fallimento falso
Rimuovere
sondaggio()
Metodo Remove, restituisce con successo l'elemento head della coda. Se non è presente alcun elemento nella coda, restituisce null ed elimina l'elemento.
esaminare
sbirciare()
Controlla il metodo e restituisci con successo l'elemento head nella coda. Se non viene restituito null, l'elemento non verrà eliminato.
bloccare
inserire
mettere(e)
Quando la coda di blocco è piena e inserisci elementi nella coda, la coda bloccherà il thread produttore finché non inserisce i dati o esce in risposta a un'interruzione.
Rimuovere
Prendere()
Quando la coda di blocco è vuota e quindi prende elementi dalla coda, la coda bloccherà il thread del consumatore finché la coda non sarà disponibile.
esaminare
non disponibile
tempo scaduto
inserire
offerta(e, tempo, unità)
Se l'operazione tentata non può essere eseguita immediatamente, la chiamata al metodo si bloccherà finché non potrà essere eseguita, ma il tempo di attesa non supererà il valore specificato. Restituisce un valore specifico per indicare se l'operazione ha avuto successo (in genere vero/falso).
Rimuovere
sondaggio(ora, unità)
esaminare
non disponibile
Pool di thread ThreadPool
Classe dello strumento pool di thread
Esecutori
Esecutori.newFixedThreadPool()
Esecutori.newSingleThreadExecutor()
Esecutori.newCachedThreadPool();
Esecutori.newScheduledThreadPool();
Esecutori.newSingleThreadScheduledExecutor();
I thread creati dalla classe dello strumento thread causeranno l'accumulo di un numero elevato di thread, portando a OOM
Pool di thread personalizzati
nuovo ThreadPoolExecutor
corePoolSize numero di thread principali
MaximumPoolSize numero massimo di thread
keepAliveTime tempo di sopravvivenza del thread non core
TimeUnit Unità di tempo di sopravvivenza
Coda di attesa del thread BlockingQueue
I primi cinque parametri possono anche personalizzare un pool di thread
Fabbrica di fili ThreadFactory
Strategia di rifiuto di RejectedExecutionHandler
La coda è piena e anche i thread non principali sono pieni. Verranno utilizzate quattro strategie di rifiuto.
AbortPolicy
Politica di rifiuto predefinita, genera direttamente l'eccezione
callerRunsPolicy
Consegnato al thread nel pool di thread corrente per l'esecuzione
Politica di eliminazione
Eliminalo direttamente senza elaborare o generare un'eccezione.
Elimina politica più vecchia
Scarta il thread più vecchio nella coda, che è il thread in primo piano nella coda, quindi aggiungivi il nuovo thread.
Il pool di thread esegue attività di thread
eseguire ( )
È possibile passare solo le attività thread che implementano l'interfaccia Runnable
invia ( )
È possibile passare attività di thread che implementano l'interfaccia Callable
Il numero di thread del pool di thread appena creati è 0. Una volta creato il thread principale, verrà conservato per sempre.
I principi alla base dell'elevata concorrenza multi-thread
Modello di memoria JVM
Partizionamento della memoria
memoria principale
Tutte le variabili vengono salvate
memoria di lavoro
Ogni thread ha la propria memoria di lavoro, che è esclusiva del thread e memorizza una copia delle variabili utilizzate dal thread (una copia delle variabili condivise della memoria principale). La memoria di lavoro è responsabile dell'interazione con i thread e anche con la memoria principale.
variabili condivise
Se una variabile viene utilizzata da più thread, questa variabile manterrà una copia nella memoria di lavoro di ciascun thread. Questo tipo di variabile è una variabile condivisa.
Tre caratteristiche principali del modello di memoria
atomicita
Cioè indivisibile, come a = 1 1 (atomicità) a (non atomicità)
sincronizzato
Classe atomica nel pacchetto java.util.concurrent
visibilità
Ogni thread ha la propria memoria di lavoro, quindi quando un thread modifica una variabile, altri thread potrebbero non essere in grado di osservare che la variabile è stata modificata.
finale
volatile
sincronizzato
Ordine
Java riordinerà alcune istruzioni
volatile
sincronizzato
parola chiave volatile
Funzione in ambiente multi-thread
visibilità
Questa variabile è garantita per essere visibile a tutti i thread.
Ordine
Disabilita l'ottimizzazione del riordino delle istruzioni. Le variabili con modifiche volatili eseguono un'operazione aggiuntiva "load addl $0x0, (%esp)" dopo l'assegnazione. Questa operazione equivale a una barriera di memoria.
Non viene eseguita alcuna operazione di blocco quando si accede alle variabili volatili, quindi il thread di esecuzione non verrà bloccato. Pertanto, le variabili volatili rappresentano un meccanismo di sincronizzazione più leggero rispetto alla parola chiave sychronized. Non risolve il problema dell'atomicità
principio volatile
visibilità
Quando una variabile condivisa viene modificata volatile, è garantito che ciascun thread sincronizzerà immediatamente il valore modificato della variabile nella memoria principale. Quando altri thread dovranno leggere la variabile, leggeranno il valore della variabile più recente.
Come funzionano le variabili condivise modificate dal volatile
Quando un thread opera su una variabile, la leggerà dalla memoria principale nella propria memoria di lavoro. Quando il thread modifica la variabile, le copie della variabile in altri thread che hanno letto la variabile diventeranno non valide, in modo che gli altri thread. utilizzare la variabile. Quando si scopre che è scaduta, si torna alla memoria principale per ottenere nuovamente il valore della variabile, in modo da ottenere il valore più recente.
Protocollo di coerenza della cache MESI
linea della cache
La più piccola unità di archiviazione che può essere allocata nella cache della CPU. Le variabili nella cache vengono archiviate nelle righe della cache.
L'idea centrale di MESI è che quando la CPU scrive su una variabile e scopre che la variabile è una variabile condivisa, avviserà le altre CPU di impostare la riga della cache della variabile su uno stato non valido. Quando altre CPU scoprono che la riga della cache di questa variabile non è valida durante l'utilizzo di una variabile, rileggeranno l'ultima variabile dalla memoria principale.
Ordine
Proibisci il riordino delle istruzioni impostando barriere di memoria
scrivere la barriera della memoria
Leggi la barriera della memoria
Barriera universale della memoria
CAS
spiegare
Confronta e scambia. Confrontare e scambiare significa che CAS è un algoritmo di blocco ottimistico che risolve i problemi di sicurezza della concorrenza multi-thread.
Parametri di base
Indirizzo di memoria A
vecchio valore B
nuovo valore C
La sua funzione è confrontare il contenuto dell'indirizzo di memoria A specificato con il vecchio valore B. Se sono uguali, sostituisci il suo contenuto con il nuovo valore C fornito nell'istruzione; se non sono uguali, l'aggiornamento fallisce.
AQS
AbstractQueuedSynchronizer Il sincronizzatore di coda astratto è denominato AQS. È il componente di base (framework) per l'implementazione dei sincronizzatori.
Serratura
Semaforo
Conteggio alla rovescia
Barriera ciclica
Ottenuto tramite AQS. L'utilizzo specifico è implementare il proprio metodo modello ereditando AQS e quindi utilizzare la sottoclasse come classe interna del componente di sincronizzazione
Componenti principali
Lo stato della variabile della risorsa condivisa è 0 e la serratura è disponibile. Se è 1, significa che la serratura è occupata.
Coda di attesa del thread FIFO (first-in-first-out).
Idee per implementare serrature basate su AQS
tryAcquire(int): modalità esclusiva. Prova a ottenere la risorsa, restituisci vero se ha successo, falso se fallisce
tryRelease(int): modalità esclusiva. Prova a rilasciare la risorsa, restituendo true in caso di successo, false in caso di fallimento.
tryAcquireShared(int): metodo di condivisione. Prova a ottenere risorse. Un numero negativo indica un fallimento; 0 indica un successo, ma non ci sono risorse disponibili rimanenti; un numero positivo indica un successo e ci sono risorse rimanenti.
tryReleaseShared(int): modalità di condivisione. Prova a rilasciare la risorsa Se è consentito riattivare il successivo nodo in attesa dopo il rilascio, restituisce true, altrimenti restituisce false.
sHeldExclusively(): indica se il thread occupa esclusivamente le risorse. Solo quando viene utilizzata la condizione è necessario implementarla.
Principi di implementazione di blocco, sblocco e attesa di AQS
Il blocco e lo sblocco sono in realtà AQS che utilizzano CAS per modificare i propri attributi di stato.
La coda di riattivazione si chiama park() e unpark() di Unsafe
Blocco ingiusto di NonfairSync
1. Trova compareAndSetState e provalo.
2. Acquisizione fallita acquisire(1);
1. tryAcquire: prendi prima se (compareAndSetState(0, acquisisce)) prendi direttamente
2. acquireQueued: impossibile unirsi al team
Blocco equo FairSync
acquire(1); Poiché non c'è cattura casuale a causa del fair lock, acquire(1) viene utilizzato per l'accodamento.
1. tryAcquire if (!hasQueuedPredecessors() && compareAndSetState(0, acquisis)) accoda prima
2. acquireQueued:
Blocca aggiornamento e downgrade
Downgrade
Il downgrade del blocco significa il downgrade da un blocco di scrittura a un blocco di lettura. Quando il thread corrente possiede il blocco di scrittura, il processo di acquisizione di nuovo del blocco di lettura e successivamente di rilascio del blocco di scrittura è il downgrade del blocco.
Aggiornamento (solo per sincronizzato l'efficienza della CPU rallenta con l'aggiornamento del blocco)
1. Non esiste competizione tra thread e il bit del contrassegno di blocco nell'intestazione dell'oggetto è contrassegnato come blocco parziale.
Preferisce il blocco del primo thread per ottenerlo. Il blocco non verrà rilasciato una volta completata l'attività di sincronizzazione. Se viene utilizzato per la seconda volta, è ancora il thread corrente e non è necessario mantenere il blocco lo stato della parola Mark dell'intestazione dell'oggetto è 01 ed è uno stato di blocco parziale. Determina se l'ID del thread registrato è l'ID del thread corrente. In tal caso, quando questo thread entra nel blocco di sincronizzazione in futuro, CAS non è richiesto bloccaggio.
2. Se c'è concorrenza tra i thread, aggiornare il blocco parziale con un blocco leggero
Blocco leggero: JVM utilizza while(true) [spin] per ottenere il blocco Lo scopo è che la CPU afferri rapidamente un blocco.
Afferrare il blocco dello spin del filo
3. Se un thread gira dieci volte senza afferrare il lucchetto, verrà aggiornato a un lucchetto pesante.
Blocco pesante: JVM utilizza il meccanismo di riattivazione in attesa del thread per acquisire il blocco. La JVM non utilizzerà i meccanismi di attesa e notifica a meno che non sia assolutamente necessario. I thread vengono messi in pausa, cambiati e ripresi molto lentamente.
Il filo resta sospeso in attesa di essere risvegliato
Come pianificare il numero di thread
Thread core = numero di CPU * 2, thread non core = numero di CPU * 4
tempesta di autobus
Volatile è scritto troppo. Sono presenti troppi thread e alla CPU viene notificato di modificare i dati memorizzati nella cache.
In attesa di elaborazione
Vengono introdotti e contattati rispettivamente gli spin lock sincronizzati, i bias lock, i light lock e i heavyweight lock.