2009-11-30 10 views
10

ho qualche codice Java che ottiene e imposta un attributo di sessione:Utilizzo di request.getSession() come oggetto di blocco?

Object obj = session.getAttribute(TEST_ATTR); 
if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
} 

Al fine di rendere questo codice thread-safe, mi piacerebbe avvolgerlo in un blocco sincronizzato. Ma cosa utilizzo come oggetto di blocco? Ha senso usare la sessione?

synchronized (session) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 

risposta

3

Generalmente disapprovazione è utilizzare un blocco su cui non si ha il controllo. Un blocco deve essere definito il più strettamente possibile e dato che la sessione è più o meno un oggetto globale, non si adatta alla fattura. Prova a utilizzare un blocco separato dal pacchetto java.util.concurrent.locks e applicalo alla classe.

+0

Questo non è un doppio controllo, questo è solo mettere un valore in una mappa se non esiste già. – Yishai

+0

D'accordo, è presto. Eliminata. – Kevin

+0

Rendere il blocco statico alla mia classe (che non è un servlet, ma una classe creata all'interno di una pagina jsp) assicura che tutte le richieste si sincronizzino sullo stesso blocco? – MCS

1

Il tuo codice non funzionerà per almeno due motivi.

1) Se la sessione non esiste, è possibile crearla facilmente due volte per lo stesso utente e presentare una brutta condizione di competizione.

2) Se la sessione non è lo stesso oggetto tra thread, non funzionerà comunque. La sessione sarà probabilmente equals() per la stessa sessione in un altro thread, ma non funzionerà.

3

Nel contesto di servlet? Le servlet possono essere distribuite su più processi, pertanto non è sempre possibile avere lo stesso oggetto di sessione. Un corollario di questo è che un contenitore di servlet può decidere di darti un oggetto di sessione diverso nello stesso processo.

IIRC, Brian Goetz ha scritto un interessante articolo sulla difficoltà di fare le cose bene con le sessioni.

Il mio consiglio: Stai alla larga dalle sessioni il più possibile e non bloccare oggetti casuali (usa un oggetto di blocco che non ha altri scopi).

+2

È questo l'articolo: http://www.ibm.com/developerworks/library/j-jtp09238.html? Discute usa un blocco sincronizzato ma non è cosa usare come blocco. – MCS

+0

Penso che giunga alla conclusione che non esiste un blocco sicuro. –

3

Ho dato un'occhiata all'articolo che hai postato. Si potrebbe saltare la sincronizzazione tutti insieme e prendere lo stesso approccio che l'autore ha fatto utilizzando compare-and-set per garantire che i dati siano corretti:

ServletContext ctx = getServletConfig().getServletContext(); 
AtomicReference<TYPE> holder 
    = (AtomicReference<TYPE>) ctx.getAttribute(TEST_ATTR); 
while (true) { 
    TYPE oldVal = holder.get(); 
    TYPE newVal = computeNewVal(oldVal); 
    if (holder.compareAndSet(oldVal, newVal)) 
     break; 
} 

holder.compareAndSet (vecchio, nuovo) restituirà false se un po ' l'altro thread ha aggiornato il valore di "titolare" dall'ultima lettura. holder.compareAndSet (,) viene inserito in un ciclo while (true) in modo che se il valore cambia prima di poterlo scrivere, si ha la possibilità di leggere nuovamente il valore e riprovare la scrittura.

http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/AtomicReference.html

2

Le specifiche non garantisce che questo aiuterà a tutti:

synchronized (session) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 

(potrebbe funzionare per implementazioni specifiche, ma non ci sono garanzie che funzionerà in tutti i contenitori.)

Servlet 2.5 MR6 dice:

Più servlet che eseguono i thread di richiesta possono avere accesso attivo allo stesso oggetto sessione nello stesso momento. Il contenitore deve garantire che la manipolazione delle strutture di dati interne che rappresentano gli attributi di sessione venga eseguita in modo thread-safe. Lo Sviluppatore ha la responsabilità per l'accesso a thread-safe agli oggetti degli attributi stessi.Questo proteggerà la collezione di attributi all'interno dell'oggetto HttpSession dall'accesso simultaneo, eliminando l'opportunità per un'applicazione di danneggiare quella raccolta.

Fondamentalmente, la specifica lo rende un problema. La soluzione dovrà essere adattata alla progettazione dell'applicazione e al piano di implementazione. Non sono sicuro che esista una soluzione globale al problema che funzionerà in tutti i casi; tuttavia, è possibile ottenere una risposta migliore se si è più specifici sull'architettura dell'applicazione e sulla configurazione del server delle applicazioni.

1

Non è necessario bloccare poiché session.setAttribute() è thread-safe (vedere il commento delle specifiche della servlet da @McDowell sopra).

Tuttavia, utilizziamo un esempio diverso. Supponiamo che tu voglia controllare il valore dell'attributo, quindi aggiornarlo se < = 100. In questo caso dovrai sincronizzare il blocco di codice per lo il confronto < = 100 e lo setAttribute().

Ora, che cosa dovresti usare per il lucchetto? Ricorda che non vi è alcuna sincronizzazione se vengono utilizzati oggetti diversi per il blocco. Quindi blocchi di codice diversi devono utilizzare lo stesso oggetto. La scelta dell'oggetto session potrebbe essere perfetta. Ricorda anche che diversi blocchi di codice possono accedere alla sessione (sia in lettura/scrittura) anche se hai preso un blocco, a meno che quell'altro codice si blocchi anche sull'oggetto di sessione. Un trabocchetto qui è che troppi punti nel codice prendono un blocco sull'oggetto di sessione e quindi devono attendere. Ad esempio, se il tuo blocco di codice utilizza l'attributo di sessione A e un altro blocco di codice utilizza l'attributo di sessione B, sarebbe bello se non dovessero attendere l'uno sull'altro entrambi bloccando l'oggetto di sessione. L'uso di oggetti statici chiamati LockForA e LockForB potrebbe essere una scelta migliore per il tuo codice da utilizzare, ad es. synchronized (LockForA) { }.

0

Ho avuto lo stesso problema e non volevo portarlo nella mia classe perché la creazione del mio oggetto poteva richiedere un secondo e bloccare i thread di altre sessioni in cui l'oggetto era già stato creato. Non volevo utilizzare l'oggetto di sessione della richiesta da sincronizzare perché l'implementazione del server delle applicazioni potrebbe restituire una facciata di sessione diversa in richieste diverse e la sincronizzazione su oggetti diversi semplicemente non funziona. Così ho scelto di mettere un oggetto nella sessione da usare come blocco e usare l'idioma di doppio controllo per garantire che il blocco venga creato una sola volta. La sincronizzazione nell'ambito di MyObject non è più un problema perché la creazione di un oggetto è piuttosto veloce.

Object lock = session.getAttribute("SessionLock"); 
if (lock == null) { 
    synchronized (MyObject.class) { 
    lock = session.getAttribute("SessionLock"); 
    if(lock == null) { 
     lock = new Object(); 
     session.setAttribute("SessionLock", lock); 
    } 
    } 
} 
synchronized (lock) { 
    Object obj = session.getAttribute(TEST_ATTR); 
    if (obj==null) { 
    obj = new MyObject(); 
    session.setAttribute(obj); 
    } 
} 
Problemi correlati