ReadWriteLock per scenari lettore-scrittore
Utilizzi ReadWriteLock per consentire letture concorrenti garantendo al contempo scritture esclusive in una cache.
ReadWriteLock per scenari lettore-scrittore è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.
Il problema dei lettori e degli scrittori
Più thread possono leggere in sicurezza dati condivisi contemporaneamente. La scrittura, invece, richiede un accesso esclusivo: non sono consentite letture o scritture concorrenti. Un ReadWriteLock modella questo comportamento: più lettori concorrenti OPPURE un solo scrittore esclusivo.
import java.util.concurrent.locks.*;
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();Read lock: accesso condiviso
Più thread possono detenere contemporaneamente il read lock, purché nessun thread detenga il write lock:
class ReadableCache {
private final Map<String, String> cache = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
String get(String key) {
lock.readLock().lock();
try {
return cache.get(key); // concurrent reads OK
} finally {
lock.readLock().unlock();
}
}
}Write lock: accesso esclusivo
Alla volta, un solo thread può detenere il write lock. Durante una scrittura, tutti i lettori e gli altri scrittori vengono bloccati:
class WritableCache extends ReadableCache {
private final ReadWriteLock wLock = new ReentrantReadWriteLock();
private final Map<String, String> data = new HashMap<>();
void put(String key, String value) {
wLock.writeLock().lock();
try {
data.put(key, value); // exclusive write
} finally {
wLock.writeLock().unlock();
}
}
}Esempio completo di cache
Una cache thread-safe in cui le letture sono prevalenti e le scritture sono rare:
class Cache<K,V> {
private final Map<K,V> map = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
V get(K key) {
lock.readLock().lock();
try { return map.get(key); }
finally { lock.readLock().unlock(); }
}
void put(K key, V value) {
lock.writeLock().lock();
try { map.put(key, value); }
finally { lock.writeLock().unlock(); }
}
int size() {
lock.readLock().lock();
try { return map.size(); }
finally { lock.readLock().unlock(); }
}
}Downgrade del lock
ReadWriteLock supporta il downgrade del lock: acquisire il write lock → acquisire il read lock → rilasciare il write lock. In questo modo un thread può passare da una scrittura esclusiva a una lettura condivisa senza rilasciare il lock:
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
Lock r = rwl.readLock(), w = rwl.writeLock();
w.lock(); // acquire write
try {
// modify data
r.lock(); // acquire read WHILE holding write
} finally {
w.unlock(); // release write, now holding read only
}
try {
// safely read the just-written data
} finally {
r.unlock();
}Il read lock non supporta l'upgrade
L'upgrade del lock (da read a write) NON è supportato. Se un thread che detiene un read lock tenta di acquisire il write lock, si verifica un deadlock perché il write lock attende tutti i lettori, incluso il thread stesso:
// DEADLOCK: Do NOT do this
lock.readLock().lock();
try {
lock.writeLock().lock(); // DEADLOCK — waits for readLock to release
} finally {
lock.readLock().unlock();
}Quando ReadWriteLock è utile
ReadWriteLock è vantaggioso quando:
- Le letture sono significativamente più numerose delle scritture
- Le operazioni di lettura richiedono un tempo non trascurabile, ad esempio per query complesse
Può ridurre le prestazioni quando le scritture sono frequenti (il write lock blocca tutti i lettori) oppure quando le operazioni sono molto rapide (per l'overhead dei due oggetti lock).
Confronto tra ReadWriteLock e synchronized
synchronized/ReentrantLock → un solo thread alla volta (anche le letture concorrenti vengono bloccate).
ReadWriteLock → più lettori concorrenti e uno scrittore esclusivo. Offre un throughput di lettura migliore negli scenari con prevalenza di letture.
// synchronized: only one thread reads at a time
synchronized String get(String key) { return cache.get(key); }
// ReadWriteLock: many threads can read simultaneously
String get2(String key) {
lock.readLock().lock();
try { return cache.get(key); }
finally { lock.readLock().unlock(); }
}Considerazioni sulle prestazioni
Acquisire e rilasciare due oggetti lock aggiunge overhead rispetto a un singolo lock. Eseguire il profiling prima di dare per scontato che ReadWriteLock sia più veloce. Negli scenari con prevalenza di letture e operazioni lunghe, il vantaggio è evidente. Per carichi di lavoro rapidi e con prevalenza di scritture, un semplice ReentrantLock può essere più veloce.
Alternativa: ConcurrentHashMap
Per semplici operazioni su mappe, ConcurrentHashMap è spesso più veloce di una combinazione HashMap + ReadWriteLock, perché usa lock interni a livello di segmento senza contesa globale:
// Often better than HashMap + ReadWriteLock for simple operations:
Map<String, String> concurrent = new ConcurrentHashMap<>();
concurrent.put("key", "value"); // thread-safe, no explicit lock
String v = concurrent.get("key"); // thread-safeFairness del read lock
Per impostazione predefinita, ReentrantReadWriteLock non è fair (i lettori possono causare starvation degli scrittori). Usare il costruttore fair per impedire la starvation degli scrittori:
// Fair: waiting writers are served before new readers
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);
System.out.println(fairLock.isFair()); // trueVerifica rapida
In un'applicazione con prevalenza di letture, qual è il principale vantaggio di ReadWriteLock rispetto a un semplice metodo synchronized?
Riepilogo: ReadWriteLock
Punti chiave:
- Più letture concorrenti OPPURE una sola scrittura esclusiva, non entrambe
- readLock.lock()/unlock() per le letture condivise
- writeLock.lock()/unlock() per le scritture esclusive
- Il downgrade del lock è supportato; l'upgrade (da read a write) causa un deadlock
- Ideale per carichi con prevalenza di letture e operazioni non trascurabili; usare ConcurrentHashMap per le mappe semplici
Domande Frequenti
La lezione «ReadWriteLock per scenari lettore-scrittore» è gratuita?
Sì — il testo completo di «ReadWriteLock per scenari lettore-scrittore» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.
Cosa imparerò in «ReadWriteLock per scenari lettore-scrittore»?
Utilizzi ReadWriteLock per consentire letture concorrenti garantendo al contempo scritture esclusive in una cache. Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Java Academy?
Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.
Quanto tempo richiede la lezione «ReadWriteLock per scenari lettore-scrittore»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Java Academy?
Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- ReentrantLock e synchronized a confronto
- ReadWriteLock per scenari lettore-scrittore
- Variabili atomiche: aggiornamenti senza lock
- StampedLock e letture ottimistiche