2009-11-30 13 views
10

que tienen algo de código Java que obtiene y establece un atributo de sesión:¿Cómo usar request.getSession() como objeto de bloqueo?

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

Con el fin de hacer que el código seguro para subprocesos, me gustaría que envolverlo en un bloque sincronizado. Pero, ¿qué uso como objeto de bloqueo? ¿Tiene sentido usar la sesión?

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

Respuesta

3

Generalmente se desaprueba el uso de un candado sobre el que no se tiene control. Un bloqueo debe tener el alcance lo más ajustado posible y, dado que la sesión es más o menos un objeto global, no se ajusta a la factura. Intente utilizar un bloqueo separado del paquete java.util.concurrent.locks y alcance su clase.

+0

Esto no es un bloqueo doble, esto simplemente está poniendo un valor en un mapa si no existe ya. – Yishai

+0

De acuerdo, es temprano. Eliminado – Kevin

+0

¿Va a hacer que el bloqueo sea estático para mi clase (que no es un servlet, sino una clase que se crea desde una página jsp) para garantizar que todas las solicitudes se sincronicen en el mismo bloqueo? – MCS

1

Su código no funcionará por al menos dos razones.

1) Si la sesión no existe, entonces puede crearla fácilmente dos veces para el mismo usuario y tener una condición de carrera desagradable.

2) Si la sesión no es el mismo objeto entre subprocesos, entonces no funcionará de todos modos. La sesión probablemente será equals() a la misma sesión en otro hilo, pero eso no funcionará.

3

En el contexto de servlets? Los servlets se pueden distribuir a través de múltiples procesos, por lo tanto, no siempre se puede tener el mismo objeto de sesión. Un corolario de esto es que un contenedor de servlets puede decidir darle un objeto de sesión diferente en el mismo proceso.

IIRC, Brian Goetz escribió un interesante artículo sobre la dificultad de hacer las cosas bien con las sesiones.

Mi consejo: Manténgase alejado de las sesiones tanto como sea posible, y no bloquee objetos aleatorios (use un objeto de bloqueo que no tenga otro propósito).

+2

¿Es este el artículo: http://www.ibm.com/developerworks/library/j-jtp09238.html? Discute utiliza un bloque sincronizado pero no qué usar como bloqueo. – MCS

+0

Creo que llega a la conclusión de que no hay un candado seguro. –

3

Eché un vistazo al artículo que publicó. Se podría omitir la sincronización de todos juntos y tener el mismo enfoque que el autor hizo mediante el uso de comparación y establecer para asegurar que sus datos son correctos:

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 (viejo, nuevo) devolverá false si alguna otro hilo actualizó el valor de "titular" desde la última vez que lo leyó. holder.compareAndSet (,) se coloca en un ciclo while (verdadero) de modo que si el valor cambió antes de que pudieras escribirlo, entonces tienes la oportunidad de volver a leer el valor y volver a intentar escribir.

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

2

La especificación no garantiza que esto ayudará en absoluto:

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

(que podría funcionar para las implementaciones específicas, pero no hay garantías de que funcione en todos los contenedores.)

Servlet 2.5 MR6 dice:

Los servlets múltiples que ejecutan subprocesos de solicitud pueden tener acceso activo al mismo objeto de sesión al mismo tiempo. El contenedor debe garantizar que la manipulación de las estructuras de datos internas que representan los atributos de la sesión se realice de forma segura para los hilos. El Desarrollador tiene la responsabilidad del acceso en modo de ejecución a los objetos de atributo en sí mismos.Esto protegerá la colección de atributos dentro del objeto HttpSession del acceso concurrente, eliminando la oportunidad de que una aplicación se corrompa.

Básicamente, la especificación lo convierte en su problema. Su solución deberá adaptarse al diseño de su aplicación y plan de despliegue. No estoy seguro de que haya una solución global al problema que funcione en todos los casos; sin embargo, puede obtener una mejor respuesta si es más específico sobre la arquitectura de su aplicación y la configuración de su servidor de aplicaciones.

1

No necesita bloquear ya que session.setAttribute() es seguro para la ejecución de subprocesos (consulte el comentario de la especificación de servlet de @McDowell anterior).

Sin embargo, usemos un ejemplo diferente. Digamos que quería comprobar el valor del atributo y luego actualizarlo si < = 100. En este caso, necesitaría sincronizar el bloque de código para getAttribute(), la comparación < = 100 y setAttribute().

Ahora, ¿qué deberías usar para la cerradura? Recuerde que no hay sincronización si se usan diferentes objetos para el bloqueo. Entonces, diferentes bloques de código deben usar el mismo objeto. Su elección del objeto session puede estar bien. Recuerde también que los diferentes bloques de código pueden acceder a la sesión (tanto de lectura como de escritura) incluso si ha tomado un bloqueo, a menos que ese otro código también se bloquee en el objeto de sesión. Una trampa aquí es que demasiados lugares en su código bloquean el objeto de sesión y, por lo tanto, tienen que esperar. Por ejemplo, si su bloque de código usa el atributo de sesión A y otro trozo de código utiliza el atributo de sesión B, sería bueno si no necesitaran esperar el uno al otro con un bloqueo en el objeto de sesión. El uso de objetos estáticos llamados LockForA y LockForB podría ser una mejor opción para que su código lo use, p. synchronized (LockForA) { }.

0

Tuve el mismo problema y no quería ubicarlo en mi clase porque la creación de mi objeto podría tomar un segundo y detener los hilos de otras sesiones donde el objeto ya se había creado. No quería utilizar el objeto de sesión de la solicitud para sincronizar porque la implementación del servidor de aplicaciones podría devolver una fachada de sesión diferente en diferentes solicitudes y la sincronización en diferentes objetos simplemente no funciona. Así que elijo poner un objeto en la sesión para utilizarlo como un candado y usar el idioma de verificación doble para asegurar que el candado se cree solo una vez. La sincronización en el alcance de MyObject ya no es un problema porque crear un objeto es bastante rápido.

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); 
    } 
} 
Cuestiones relacionadas