2011-07-22 11 views
11

Tengo un conjunto de preguntas relacionadas con los problemas de subprocesamiento múltiple de Java. Por favor, dame tanta ayuda como puedas.Algunas preguntas sobre multiprocesamiento en java,

0) Supongamos que tenemos 2 cuentas bancarias y tenemos que transferir dinero entre ellas de una manera segura. existen es decir

accountA.money += transferSum; 
accountB.money -= transferSum; 

dos requisitos:

  1. nadie debe ser capaz de ver los resultados intermedios de la operación (es decir, una suma acount se incrementa, pero otros aún no se disminuye)
  2. el acceso de lectura no debe bloquearse durante la operación (es decir, los valores antiguos de las sumas de la cuenta se deben mostrar durante la operación)

¿Puede usted sugerir algunas ideas sobre esto?

1) Supongamos que 2 hilos modifican algún campo de clase mediante un método sincronizado o utilizando un bloqueo explícito. Independientemente de la sincronización, no hay garantía de que este campo sea visible para los hilos, que lo lean a través del método NO sincronizado. - ¿es correcto?

2) ¿Cuánto tiempo se puede esperar un bloqueo provocado por el método de notificación de un bloqueo? Supongamos que tenemos un código como este:

synchronized(lock) { 
    lock.notifyall(); 
    //do some very-very long activity 
    lock.wait() //or the end of synchronized block 
} 

¿Podemos decir que al menos un hilo tendrá éxito y agarrar el candado? ¿Se puede perder una señal debido a un tiempo de espera?

3) Una cita de Java concurrencia del libro:

"ejecutores de un solo subproceso también proporcionan suficiente sincronización interna para garantizar que cualquier memoria escribe hecha por tareas son visibles para las tareas subsiguientes, lo que significa que los objetos pueden ser confinado de forma segura al "hilo de tarea", aunque ese hilo pueda ser reemplazado por otro de vez en cuando ".

¿Esto significa que el único problema de seguridad de subprocesos que queda para ejecutar un código en un ejecutor de subproceso único es la carrera de datos y podemos abandonar las variables volátiles y pasar por alto todos los problemas de visibilidad? Parece una forma universal de resolver gran parte de los problemas de simultaneidad.

4) Todos los getters y setters estándar son atómicos. No necesitan sincronizarse si el campo está marcado como volátil. - ¿es correcto?

5) La iniciación de campos estáticos y bloques estáticos se realiza mediante un hilo y, por lo tanto, no es necesario sincronizar. - ¿es correcto?

6) Por qué un hilo tiene que notificar a los demás si sale el bloqueo método wait() con, pero no tiene que hacer esto si sale el bloqueo al salir del bloque sincronizado?

+3

me gusta el hecho de que su índice cuestión comienza en 0 y no de 1 :) –

+0

Nota rápida sobre 0) - este es un requisito común, pero normalmente se resuelve en un nivel base de datos con las transacciones, no en una aplicación nivel. Consulte http://en.wikipedia.org/wiki/Atomic_commit – Qwerky

Respuesta

7

0: No se puede.

Asegurar una actualización atómica es fácil: se sincroniza en cualquier objeto que tenga las cuentas bancarias. Pero luego bloquea a todos los lectores (porque también se sincronizan), o no puede garantizar lo que verá el lector.

PERO, en un sistema a gran escala, como un sistema bancario, bloquear objetos frecuentemente accedidos es una mala idea, ya que introduce esperas en el sistema. En el caso específico de cambiar dos valores, esto podría no ser un problema: sucederá tan rápido que la mayoría de los accesos quedarán ocultos.

Sin duda hay maneras de evitar tales condiciones de carrera. Las bases de datos hacen un buen trabajo para las cuentas de nómina (aunque en última instancia se basan en el acceso disputado al final de una transacción).

1) Que yo sepa, no hay garantías distintas a las establecidas por synchronized o volatile. Si un hilo realiza un acceso sincronizado y un hilo no, el acceso no sincronizado no tiene una barrera de memoria. (si estoy equivocado, estoy seguro de que me corregirán o al menos bajarán de categoría)

2) Para citar que JavaDoc: "Los hilos despertados no podrán continuar hasta que el hilo actual renuncie al bloqueo en este objeto ". Si decides dormir en ese bloque sincronizado, estarás descontento.

3) Tendría que leer esa cita varias veces para estar seguro, pero creo que el "ejecutor de un solo subproceso" es la frase clave. Si el ejecutor está ejecutando solo un hilo, entonces hay una estricta relación de pasar por delante para todas las operaciones en ese hilo. Es no significa que otros subprocesos, ejecutándose en otros ejecutores, pueden ignorar la sincronización.

4) No. long y double no son atómicos (consulte el JVM spec). Use un objeto AtomicXXX si desea acceso no sincronizado a las variables miembro.

5) No. No pude encontrar una referencia exacta en la especificación de JVM, pero la sección 2.17.5 implica que múltiples hilos pueden inicializar las clases.

6) Porque todos los hilos esperan hasta un hilo hace una notificación. Si está en un bloque sincronizado y lo deja con una espera y sin notificación, cada hilo estará esperando una notificación que nunca sucederá.

+2

Re 5), "Concurrencia de Java en la práctica" establece que la inicialización de clase (incluidos los bloques de inicialización estática y los miembros 'estáticos finales ') está protegida por la JVM, por lo que es garantizado para ser hilo de seguridad. Los miembros no finales no son vigilados, sin embargo. –

+0

@Peter - No soy de los que contradicen a Brian Goetz, pero teniendo en cuenta las advertencias sobre seguridad de subprocesos en las especificaciones de JVM, (1) me gustaría ver la cita exacta, y (2) no me sorprendería si hay una JVM que no tiene inicialización de clase de subproceso único. Sin duda, haría la vida del equipo de implementación de JVM más fácil si hicieran un solo hilo. – parsifal

+1

@parsifa, JVM utiliza el hilo de carga actual para inicializar la clase y utiliza bloqueos para garantizar la seguridad del hilo. La inicialización de un solo subproceso sería terriblemente tonto – bestsss

1

0) es un problema difícil porque no desea que los resultados intermedios que sean visibles o para bloquear los lectores durante la operación. Para ser sincero, no estoy seguro de que sea posible, para garantizar que ningún hilo vea los resultados intermedios que necesita para bloquear a los lectores mientras realiza ambas escrituras.

Si no desea que aparezcan resultados intermedios, debe cerrar las dos cuentas anteriores antes de escribir. La mejor manera de hacerlo es asegurarse de obtener y liberar los bloqueos en el mismo orden cada vez (de lo contrario, se produce un interbloqueo). P.EJ. obtener el candado en el número de cuenta más bajo primero y luego el mayor.

1) Correcto, todo el acceso debe realizarse a través de un candado/sincronizado o usar volátil.

2) siempre

3) El uso de un único subproceso Ejecutor significa que mientras todos los accesos se doen por las tareas que ejecuta ese ejecutor usted no necesita preocuparse por la seguridad de rosca/visibilidad.

4) No estoy seguro de lo que quiere decir con getters y setters estándar pero escribe en la mayoría de los tipos variables (excepto dobles y largos) son atómicos y no necesitan sincronización, solo son volátiles para la visibilidad. Intenta usar las variantes atómicas en su lugar.

5) No, es posible que dos subprocesos prueben un código estático de inicio, por lo que las implementaciones ingenuas de Singleton son inseguras.

6) Sincronización y Esperar/Notificar son dos mecanismos diferentes pero relacionados. Sin esperar/notificar que tendrías que girar el bloqueo (es decir, seguir obteniendo un bloqueo y un sondeo) en un objeto para obtener actualizaciones

+0

0 - es posible, necesita máquina de estados y versiones clonadas de las cuentas + control de versiones. Sin embargo, el problema principal es qué sucede con los datos de lectura, ya que puede ser inconsistente en cualquier momento. – bestsss

+0

Supongo que mi punto es que si no quiere ningún bloqueo y no quiere ninguna incoherencia de lo que no es posible. – brain

+0

posiblemente puede girar hasta que tenga éxito, pero en ese caso las lecturas/escrituras son más fáciles de implementar y hacerlo bien – bestsss

0

0) La única forma en que puedo ver esto para almacenar cuentas A y cuentasB en un objeto almacenado en una AtomicReference. A continuación, realiza una copia del objeto, lo modifica y actualiza la referencia si sigue siendo la misma que la referencia original.

AtomicReference<Accounts> accountRef; 

Accounts origRef; 
Accounts newRef; 
do { 
    origRef = accountRef.get(); 
    // make a deep copy of origRef 

    newRef.accountA.money += transferSum; 
    newRef.accountB.money -= transferSum; 
} while(accountRef.compareAndSet(origRef, newRef); 
1

5) La iniciación de campos estáticos y bloques estáticos se realiza mediante un hilo y, por lo tanto, no es necesario sincronizar. - ¿es correcto?

VM ejecuta la inicialización estática en un bloque sincronizado (clazz).

static class Foo { 
    static { 
     assert Thread.holdsLock(Foo.class); // true 

     synchronized(Foo.class){ // redundant, already under the lock 
      .... 
Cuestiones relacionadas