Qual è il ragionamento alla base della doppia chiamata a WeakHashMap.put (..)?
-
05-07-2019 - |
Domanda
Questo post di blog dimostra un modo di implementare un idioma id mutex per stringa. Gli ID stringa utilizzati devono rappresentare gli ID HttpSession.
- Perché dobbiamo includere un WeakReference attorno alle istanze di Mutex? Non è meglio creare semplicemente una mappa da String - > Mutex?
-
Perché dobbiamo chiamare put due volte?
public Mutex getMutex( String id ) { Mutex key = new MutexImpl( id ); synchronized( mutexMap ) { WeakReference<Mutex> ref = mutexMap.get( key ); if( ref == null ) { mutexMap.put( key, new WeakReference<Mutex>( key ) ); return key; } Mutex mutex = ref.get(); if( mutex == null ) { mutexMap.put( key, new WeakReference<Mutex>( key ) ); return key; } return mutex; } }
Soluzione
Loop e Bruno Conde l'ho praticamente coperto, ma da quando ho scritto quel codice ...
L'obiettivo del progetto era evitare che l'utente chiamasse un meccanismo di rilascio: il mutex è idoneo per la garbage collection quando l'utente non lo fa più riferimento.
Perché dobbiamo avvolgere un riferimento debole intorno alle istanze di Mutex?
La mappa è una WeakHashMap :
private final Map mutexMap = new WeakHashMap();
Questa mappa viene utilizzata per mantenere un riferimento al mutex, ma se si utilizza lo stesso oggetto per la chiave e il valore, l'oggetto non è idoneo per la garbage collection. Javadoc:
Nota di implementazione: gli oggetti valore in una WeakHashMap sono gestiti dall'ordinario riferimenti forti. Quindi la cura dovrebbe essere preso per garantire che gli oggetti valore lo facciano non si riferiscono fortemente alle proprie chiavi, direttamente o indirettamente, poiché ciò impedirà alle chiavi di essere scartato. Si noti che un oggetto valore può fare riferimento indirettamente alla sua chiave tramite la WeakHashMap stessa; cioè un valore oggetto può riferirsi fortemente qualche altro oggetto chiave il cui associato valore oggetto, a sua volta, si riferisce fortemente alla chiave del primo oggetto valore. Un modo per affrontare questo è quello di avvolgere si valutano all'interno Riferimenti deboli prima di inserire, come in: m.put (chiave, nuovo WeakReference (valore)) e quindi da scartare su ogni get.
Non è vero meglio creare semplicemente una mappa da String - > Mutex?
Quando verrà raccolto il valore di quella stringa? Lo stesso riferimento viene passato ogni volta? Ha intern () stato invitato? Se chiamo stagista, quanto durerà la String? Se una stringa era la chiave, il mutex potrebbe non essere idoneo per la garbage collection molto tempo dopo che non è necessario mantenerne un riferimento.
Perché dobbiamo chiamare put due volte?
Esistono due casi da gestire fino a quando il metodo non può ottenere un forte riferimento al mutex nella mappa:
- il WeakReference ha spazzatura raccolta (o non è mai stata lì in primo luogo)
- i contenuti di WeakReference sono rifiuti raccolti dopo che il riferimento è stato acquisito
put sarà invocato solo una volta; il metodo ritorna immediatamente dopo.
(The WeakReference potrebbe essere riutilizzato nella seconda frase, ma non vedo che sarebbe un miglioramento significativo.)
Naturalmente, se qualcuno riesce a trovare un difetto nel codice, fammi sapere e lo correggerò felicemente. Inoltre, i test unitari cercano di verificare che l'implementazione non perda, quindi sentiti libero di modificare il codice e vedere cosa succede quando esegui i test.
Altri suggerimenti
Gli oggetti valore in una WeakHashMap sono mantenuti da normali riferimenti forti. Pertanto, occorre prestare attenzione a garantire che gli oggetti valore non si riferiscano fortemente alle proprie chiavi, né direttamente né indirettamente, poiché ciò impedirà che le chiavi vengano scartate. Si noti che un oggetto valore può fare riferimento indirettamente alla sua chiave tramite la stessa WeakHashMap; vale a dire, un oggetto valore può riferirsi fortemente a qualche altro oggetto chiave il cui oggetto valore associato, a sua volta, si riferisce fortemente alla chiave del primo oggetto valore. Un modo per gestirlo è avvolgere i valori all'interno di WeakReferences prima di inserirli, come in: m.put (chiave, nuovo WeakReference (valore)), e quindi scartarli ad ogni get.
1 - @Loop ha un buona risposta .
2 - Supponendo che le voci siano racchiuse in WeakReferences, il secondo put
è necessario perché WeakReference
può essere raccolto prima che l'esecuzione raggiunga la linea:
Mutex mutex = ref.get();
In questo caso:
- la voce potrebbe non esistere già nella
map
- se esiste, può essere raccolto prima dell'esecuzione di
Mutex mutex = ref.get ();
Direi che i WeakReference
sono usati solo perché devono fare riferimento agli ID di sessione http. Non puoi sempre essere sicuro al 100% di ricevere una notifica che termina una sessione in modo da causare una mappa in continua crescita.
La prima volta che chiami put è perché la mappa non contiene la tua chiave. La seconda volta è perché la mappa conteneva la chiave ma il riferimento non esisteva più.