2009-10-30 9 views
19

Cada Java Object tiene los métodos wait() y notify() (y variantes adicionales). Nunca los he usado y sospecho que muchos otros no. ¿Por qué son tan fundamentales que cada objeto tiene que tenerlos y hay un impacto en el rendimiento al tenerlos (presumiblemente algún estado se almacena en ellos)?¿Por qué todos los objetos Java tienen wait() y notify() y esto causa un golpe de rendimiento?

EDIT para enfatizar la pregunta. Si tengo un List<Double> con 100.000 elementos, entonces cada Double tiene estos métodos, ya que se extiende desde Object. Pero parece poco probable que todos estos tengan que saber sobre los hilos que gestionan el List.

EDIT respuestas excelentes y útiles. @Jon tiene una publicación de blog muy buena que cristalizó mis sentimientos viscerales. También estoy de acuerdo completamente con @Bob_Cross en que debes mostrar un problema de rendimiento antes de preocuparte por ello. (También como la enésima ley de idiomas exitosos si hubiera sido un golpe de rendimiento, Sun o alguien lo habría solucionado).

+0

Su ejemplo no está bien elegido, ya que String es inmutable y, por lo tanto, no tiene estado, lo que es relativamente poco frecuente. –

+0

@Kevin - gracias. Editaré –

Respuesta

32

Bueno, sí significa que cada objeto tiene que potencialmente tienen un monitor asociado a él. El mismo monitor se usa para synchronized. Si está de acuerdo con la decisión de poder sincronizar en cualquier objeto, entonces wait() y notify() no agregan ningún otro estado por objeto. La JVM puede asignar el monitor real de forma perezosa (sé que lo hace .NET), pero debe haber espacio de almacenamiento disponible para decir qué monitor está asociado con el objeto. Es cierto que es posible que sea una cantidad muy pequeña (p. Ej., 3 bytes) que en realidad no guardará ninguna memoria debido al relleno del resto de la sobrecarga del objeto. Tendría que observar cómo cada JVM individual manejaba la memoria para decir sin lugar a duda.

Tenga en cuenta que sólo tener métodos adicionales no afecta al rendimiento (que no sea muy ligeramente debido al código obvia estar presente en algún lugar ). No es que cada objeto o incluso cada tipo tenga su propia copia del código para wait() y notify(). Dependiendo de cómo funcionan las tablas virtuales, cada tipo puede terminar con una entrada vtable adicional para cada método heredado, pero eso sigue siendo solo por tipo, no por objeto. Eso básicamente se va a perder en el ruido en comparación con la mayor parte del almacenamiento que es para los mismos objetos.

Personalmente, siento que ambos.NET y Java cometieron un error al asociar un monitor con cada objeto; prefiero tener objetos explícitos de sincronización. Escribí un poco más sobre esto en un blog post about redesigning java.lang.Object/System.Object.

+1

@Jon gracias, este también fue mi punto de vista. –

+1

punto de acceso ni siquiera puede asignar un monitor para el objeto, a menos que cambie de manos. (IBM VM es lo mismo también). Si es así, infla el encabezado del objeto y asigna el mutex. Sincronización generalmente no descontada. Es muy barata (http://java.sun.com/performance/reference/whitepapers/6_performance.html#2.1.1). También wait/notify son finales (y en su mayoría nativos), por lo que tampoco forman parte de ninguna VTable. – bestsss

+0

@bestsss: ¿Qué quiere decir exactamente con "inflar el encabezado del objeto"? Es el encabezado del objeto de tamaño variable, entonces? Yikes. En .NET, creo que el monitor solo se asigna a pedido (y de nuevo, si nunca hay una disputa, generalmente no necesita asignar una) pero no creo que cambie el tamaño del encabezado del objeto. –

1

Estos métodos se utilizan para implementar la comunicación entre hilos.

Verificar this article on the subject.

Reglas para esos métodos, tomados de ese artículo:

  • wait() indica al subproceso de la llamada a abandonar el monitor e ir a dormir hasta que algún otro hilo entra en el mismo monitor y llamadas notificar() .
  • notify() se activa el primer hilo que llamó a wait() en el mismo objeto.
  • notifyAll() activa todos los hilos que llamaron a wait() en el mismo objeto. El subproceso de prioridad más alta se ejecutará primero.

Espero que esto ayude ...

+3

No hacen comunicación entre * procesos *, hacen una coordinación entre hilos *. –

+0

thx para corregir. – KB22

+0

Allí. Corregido –

2

Todos los objetos en Java tienen monitores asociados. Las primitivas de sincronización son útiles en casi todos los códigos de subprocesos múltiples, y es semánticamente muy agradable sincronizar en los objetos a los que está accediendo en lugar de objetos separados de "Monitor".

Java puede asignar los monitores asociados con los objetos según sea necesario, como lo hace .NET, y en cualquier caso la sobrecarga real para simplemente asignar (pero no usar) el bloqueo sería bastante pequeño.

En resumen: es muy práctico para almacenar objetos con sus bits de soporte de seguridad de hilo, y hay muy poco impacto en el rendimiento.

+0

No creo que sea realmente así de conveniente: la práctica recomendada recomienda encapsular bloqueos al no exponerlos fuera de su clase a menos que sea necesario (por ejemplo, no bloquee "this" o "Foo.class"). Sería más elegante * y * un poco más eficiente en almacenamiento que no tener un monitor potencialmente asociado con cada clase. –

+0

Ehhh ... si expone regularmente campos de una clase, tiene problemas además de la eficiencia de bloqueo. Estoy de acuerdo en que podría guardar algo de memoria declarando explícitamente objetos de bloqueo, pero eso debería ser del orden de sizeof (void *) o sizeof (int) por objeto (suponiendo una inicialización lenta); si estás preocupado, probablemente no deberías enlazar en un tiempo de ejecución tan grande como la JVM. –

12

¿Por qué estas tan fundamental que cada objeto tiene que tener ellos y es hay un impacto en el rendimiento en disponer de ellos (presumiblemente algún estado se almacena en ellos)?

tl; dr: Son métodos de seguridad de hilos y tienen un costo pequeño en relación con su valor.

Las realidades fundamentales que these methods apoyo son que:

  1. Java es siempre multi-hilo. Ejemplo: revise la lista de subprocesos utilizados por un proceso utilizando jconsole o jvisualvm en algún momento.
  2. La corrección es más importante que el "rendimiento". Cuando estaba evaluando proyectos (hace muchos años), solía tener que explicar "llegar a la respuesta incorrecta realmente rápido sigue siendo incorrecto".

Fundamentalmente, estos métodos proporcionan algunos de los ganchos para administrar los monitores por objeto utilizados en la sincronización. Específicamente, si tengo synchronized(objectWithMonitor) en un método particular, puedo usar objectWithMonitor.wait() para obtener ese monitor (por ejemplo, si necesito otro método para completar un cálculo antes de poder proceder). En ese caso, eso permitirá que otro método sea bloqueado esperando que ese monitor proceda.

Por otro lado, puedo usar objectWithMonitor.notifyAll() para dejar que los hilos que están esperando el monitor sepan que voy a renunciar al monitor pronto. Sin embargo, no pueden continuar hasta que abandone el bloque sincronizado.

Con respecto a ejemplos específicos (por ejemplo, largas listas de dobles) donde es posible que se preocupan de que hay un rendimiento o la memoria golpeado en el mecanismo de vigilancia, aquí hay algunos puntos que usted debe probablemente considerar:

  1. Primera , Pruébalo. Si cree que hay un impacto importante de un mecanismo central de Java, como la corrección de múltiples subprocesos, existe una gran posibilidad de que su intuición sea falsa. Mida el impacto primero. Si es grave y sabe que no necesitará sincronizar en un Doble individual, considere usar dobles.
  2. Si no está seguro de que usted, su compañero de trabajo, un futuro programador de mantenimiento (que podría ser usted mismo un año después), etc.nunca necesitará alguna vez necesita una gran granularidad de acceso a sus datos, hay una gran posibilidad de que quitar estos monitores solo haga que su código sea menos flexible y fácil de mantener.

Seguimiento en respuesta a la pregunta sobre vs. objetos explícitos de monitor por cada objeto:

Respuesta corta: @JonSkeet: sí, la eliminación de los monitores crearía problemas: se crearía fricción. Mantener esos monitores en Object nos recuerda que esto es siempre un sistema multiproceso.

Los monitores de objetos incorporados no son sofisticados pero son: fáciles de explicar; trabajar de una manera predecible; y son claros en su propósito. synchronized(this) es una clara declaración de intenciones. Si forzamos a los programadores principiantes a usar el paquete de concurrencia exclusivamente, introducimos la fricción. ¿Qué hay en ese paquete? ¿Qué es un semáforo? Tenedor-unirse?

Un codificador novato puede usar los monitores Object para escribir código decente modelo-vista-controlador. synchronized, wait y notifyAll se puede utilizar para implementar ingenuo (en el sentido de simple, accesible, pero tal vez no de última generación) seguridad de hilos. El ejemplo canónico sería uno de estos Dobles (postulados por el OP) que puede tener un subproceso establecido un valor mientras que el subproceso AWT obtiene el valor para colocarlo en un JLabel. En ese caso, no hay una buena razón para crear un Objeto adicional explícito solo para tener un monitor externo.

En un nivel ligeramente más alto de complejidad, estos mismos métodos son útiles como un método de monitoreo externo. En el ejemplo anterior, explícitamente hice eso (vea los fragmentos de objectWithMonitor arriba). Una vez más, estos métodos son realmente útiles para armar seguridad de hilo relativamente simple.

Si desea ser aún más sofisticado, creo que debería pensar seriamente en leer Java Concurrency In Practice (si no lo ha hecho). Los bloqueos de lectura y escritura son muy potentes sin agregar demasiada complejidad adicional.

Punchline: Utilizando métodos de sincronización básicos, puede aprovechar una gran parte del rendimiento habilitado por los procesadores modernos multi-núcleo con seguridad de hilo y sin una gran sobrecarga.

+0

+1 para "Primero, pruébalo". :) –

+1

@ Aaron Digulla, gracias - personalmente, creo que debería ser la primera oración en cualquier argumento de rendimiento. –

+1

Aunque estoy de acuerdo con todos los consejos aquí, no veo ninguna conexión entre estos bits de funcionalidad fundamentalmente importantes, y que sean fundamentales * para cada objeto *. En otras palabras, ¿alguna de estas respuestas no funcionaría si tuviera que usar un objeto separado para el monitor? –

Cuestiones relacionadas