PDA

View Full Version : [Java] concorrenza in servlet


Swalke
19-06-2008, 18:50
Ciao a tutti.
Ho una action (o una servlet, poco cambia) che contiene una parte di codice in cui devo impedire l'esecuzione di più processi in contemporanea.
Trattandosi di una servlet, viene creata un'istanza diversa per ogni utente che la esegue (non è singleton).

Ho pensato di risolvere il problema in questo modo ma vorrei un vostro parere:

...
public static final String LOCK_V = "lock_ccu";

public String execute() throws Exception {
...
synchronized (LOCK_V)
{
//codice in concorrenza
...
}
...
}


In pratica eseguendo il synchronized sulla variabile statica LOCK_V impedisco che istanze diverse della servlet eseguano contemporaneamente la porzione di codice compresa nel blocco. Questo perchè LOCK_V è variabile di classe.

Che ne dite?
Non sto considerando qualcosa?

Swalke
13-07-2008, 13:01
UP.
Nessuno mi dice nulla? :cry:

smodeus
13-07-2008, 15:43
Trattandosi di una servlet, viene creata un'istanza diversa per ogni utente che la esegue (non è singleton).

Sbagliato: il servlet container mantiene per ogni servlet una UNICA instanza, e per ogni richiesta viene creato un nuovo thread che eseguirà il metodo service della servlet, ed il successivo metodo doXXX


Ciao a tutti.
Ho una action (o una servlet, poco cambia) che contiene una parte di codice in cui devo impedire l'esecuzione di più processi in contemporanea.
[...]
Ho pensato di risolvere il problema in questo modo ma vorrei un vostro parere:

...
public static final String LOCK_V = "lock_ccu";

public String execute() throws Exception {
...
synchronized (LOCK_V)
{
//codice in concorrenza
...
}
...
}


In pratica eseguendo il synchronized sulla variabile statica LOCK_V impedisco che istanze diverse della servlet eseguano contemporaneamente la porzione di codice compresa nel blocco. Questo perchè LOCK_V è variabile di classe.


In linea di massima il tuo ragionamento è esatto, ma non è applicabile nel caso delle servlet perchè queste hanno una unica instanza nell'applicazione web:
che genere di risorsa devi gestire per gli accessi concorrenti?
ti faccio un esempio:
se hai un attributo del ServletContext e devi fare in modo che le diverse servlet vi accedano in maniera concorrente, ti basta costruirti un blocco synchronized che usa come mutex il ServletContext ogni volta che si accede a quello specifico attibuto.

Swalke
13-07-2008, 22:47
In pratica la porzione di metodo che volevo sincronizzare esegue un accesso al database ed estrae il valore di una variabile. In base al suo valore la aggiorna e crea un file con il nome di questa variabile.
In pseudoicodice ciò che deve essere sincronizzato è qualcosa di simile a questo:


variabile=lettura variabile da DB;
if(variavile='a')
{
salvataggio file "a.txt";
variabile = 'b';
}
if(variavile='b')
{
salvataggio file "b.txt";
variabile = 'c';
}
if(variavile='c')
{
salvataggio file "c.txt";
variabile = 'd';
}
...
update variabile su db;


Quindi se due thread della servlet eseguono contemporaneamente questa porzione di codice potrei avere dei problemi dovuti alla concorrenza sulla variabile che viene estratta da db.

Se il thread 1 ha letto da db il valore 'a', io voglio che finisca di operare prima che un thread 2 concorrente legga la variabile da db.

Pensavo di avere risolto il problema.

Secondo te come posso risolvere?

Ed_Bunker
14-07-2008, 10:49
Quindi il tuo problema, se non sbaglio, e' quello di accedere in maniera concorrente al db ?

In tal caso penso tu possa "semplicemente" utilizzare un livello di isolamento appropriato al tuo caso, per evitare che i thread relativi alle varie richieste possano incorrere in "corse critiche".

P.S.: per quanto riguarda il "modello" dei servlet... non dovrebbe essere invocato il metodo init nel momento in cui viene caricata l'applicazione dal servlet engine ed essere, poi, creato un thread per ogni richiesta (GET o POST) ricevuta ?

Swalke
14-07-2008, 11:15
P.S.: per quanto riguarda il "modello" dei servlet... non dovrebbe essere invocato il metodo init nel momento in cui viene caricata l'applicazione dal servlet engine ed essere, poi, creato un thread per ogni richiesta (GET o POST) ricevuta ?

Quanto dici è corretto ed è quanto mi ha fatto notare anche smodeus poco più sopra.

Il fatto è che a me sembra di essermi tutelato in modo corretto sincronizzando quel blocco di codice.

I thread della servlet dovrebbero accedere in modo sincronizzato a quel blocco come mi pare di capire da qui: http://java.sun.com/docs/books/tutorial/essential/concurrency/locksync.html.

Quindi non ho ben capito cosa intende smodeus quando dice


In linea di massima il tuo ragionamento è esatto, ma non è applicabile nel caso delle servlet perchè queste hanno una unica instanza nell'applicazione web

Ed_Bunker
14-07-2008, 11:26
Se la variabile e' statica e' condivisa da tutti i thread che vengono creati per cui credo che il synchronized possa essere utilizzato con "successo".

Imho, pero', visto che il problema e' relativo all'accesso al db prenderei in considerazioni di utilizzare in maniera "piu' pulita" i livelli di isolamento del db in questione.

Swalke
14-07-2008, 11:34
ma se l'accesso al db avviene in blocco che ho sincronizzato perchè dovrei avere problemi?

smodeus
14-07-2008, 12:27
P.S.: per quanto riguarda il "modello" dei servlet... non dovrebbe essere invocato il metodo init nel momento in cui viene caricata l'applicazione dal servlet engine ed essere, poi, creato un thread per ogni richiesta (GET o POST) ricevuta ?

Il servletContainer inizializza le servlet appunto instaziando la classe chiamano il metodo init(ServletConfig) che a sua volta richiama il metodo init() (per intenderci quello che noi possiamo sovrascrivere). A questo punto la servlet è inizializzata ed ad ogni HTTP request verrà creato un nuovo thread dall'unica instanza della servlet che eseguirà il metodo service(HttpServletRequest,HttpServletResponse)
E' questo metodo che decide quale dei metodi doXXX (doGet, doPost, etc) eseguire
Quindi init() (entrambi gli overload) vengono invocati 1 sola volta nel ciclo di vita della servlet, mentre service() viene invocato ogni volta c'è una nuova richiesta



Il fatto è che a me sembra di essermi tutelato in modo corretto sincronizzando quel blocco di codice.
I thread della servlet dovrebbero accedere in modo sincronizzato a quel blocco come mi pare di capire da qui: http://java.sun.com/docs/books/tutorial/essential/concurrency/locksync.html.
Quindi non ho ben capito cosa intende smodeus quando dice

...
public static final String LOCK_V = "lock_ccu";

public String execute() throws Exception {
...
synchronized (LOCK_V)
{
//codice in concorrenza
...
}
...
}

Ma è una sola servlet che accede a questo dato? In questo caso la soluzione va bene, se invece sono + servlet, allora questa soluzione non va bene, perchè nn ti garantisce l'accesso sincrono da parte di diverse servlet, che rappresentano classi diverse.
In generale cmq cercherei di non introdurre blocchi syncrhonized nelle servlet, ovviamente poi dipende anche dai "carichi" che potresti avere su quella servlet (quanti client posso contemporaneamente richiedere quella servlet)

Swalke
14-07-2008, 14:17
Ma è una sola servlet che accede a questo dato? In questo caso la soluzione va bene, se invece sono + servlet, allora questa soluzione non va bene, perchè nn ti garantisce l'accesso sincrono da parte di diverse servlet, che rappresentano classi diverse.

Si, in realtà il blocco è proprio all'iterno della servlet.
In realtà ho commesso un errore e chiedo perdono:
il metodo che ho chiamato "execute" nel mio esempio è in realtà un doGet o un doPost.

Dunque l'intestazione del mio metodo di esempio sarebbe la classica

public void doGet(HttpServletRequest request,
HttpServletResponse response)...

Quindi il blocco sincronizzato è all'interno del metodo della servlet che risponde alla richiesta.
Dunque la mia sincronizzazione è corretta vero?


In generale cmq cercherei di non introdurre blocchi syncrhonized nelle servlet, ovviamente poi dipende anche dai "carichi" che potresti avere su quella servlet (quanti client posso contemporaneamente richiedere quella servlet)

Su questo siamo d'accordo, la mia era più che altro una richiesta a scopo didattico.