java - ¿Crea la JVM una exclusión mutua para cada objeto con el fin de implementar la palabra clave ''sincronizada''? Si no, ¿cómo?
c++ synchronization (4)
¿No puede JVM usar instrucciones de comparar y cambiar directamente? digamos que cada objeto tiene un campo lockingThreadId
almacena el id del hilo que lo está bloqueando,
while( compare_and_swap (obj.lockingThreadId, null, thisThreadId) != thisTheadId )
// failed, someone else got it
mark this thread as waiting on obj.
shelf this thead
//out of loop. now this thread locked the object
do the work
obj.lockingThreadId = null;
wake up threads waiting on the obj
este es un modelo de juguete, pero no parece demasiado caro y no depende del sistema operativo.
Como un programador de C ++ que se está familiarizando con Java, es un poco extraño para mí ver el soporte a nivel de lenguaje para bloquear objetos arbitrarios sin ningún tipo de declaración de que el objeto admite dicho bloqueo. Crear mutexes para cada objeto parece ser un alto costo en el que se puede optar automáticamente. Además del uso de la memoria, las exclusiones mutuas son un recurso limitado del sistema operativo en algunas plataformas. Podría girar la cerradura si no hay mutexes disponibles, pero las características de rendimiento son significativamente diferentes, lo que esperaría que perjudicara la previsibilidad.
¿Es la JVM lo suficientemente inteligente en todos los casos para reconocer que un objeto en particular nunca será el objetivo de la palabra clave sincronizada y así evitar la creación del mutex? Los mutex podrían crearse perezosamente, pero eso plantea un problema de arranque que, en sí mismo, requiere un mutex, e incluso si se resolviera, supongo que todavía habrá algunos costos adicionales para rastrear si un mutex ya se ha creado o no. Así que supongo que si tal optimización es posible, debe hacerse en tiempo de compilación o inicio. En C ++, tal optimización no sería posible debido al modelo de compilación (no se podría saber si el bloqueo para un objeto se iba a usar a través de los límites de la biblioteca), pero no sé lo suficiente sobre la compilación y el enlace de Java para saber si se aplican las mismas limitaciones.
Esto es realmente un detalle de implementación de la JVM, y diferentes JVM pueden implementarlo de manera diferente. Sin embargo, definitivamente no es algo que pueda optimizarse en tiempo de compilación, ya que Java se vincula en tiempo de ejecución, y es posible que un código previamente desconocido obtenga un objeto creado en un código anterior y comience a sincronizarlo.
Tenga en cuenta que en la jerga de Java, la primitiva de sincronización se denomina "monitor" en lugar de exclusión mutua, y está soportada por operaciones de código de byte especiales. Hay una explicación bastante detallada here .
Hablando como alguien que ha visto la forma en que algunas JVM implementan bloqueos ...
El enfoque normal es comenzar con un par de bits reservados en la palabra de encabezado del objeto. Si el objeto nunca está bloqueado, o si está bloqueado pero no hay contención, permanece así. Cuando ocurre una disputa sobre un objeto bloqueado, la JVM infla el bloqueo en una estructura de datos de exclusión mutua en toda regla, y permanece así durante toda la vida útil del objeto.
EDITAR - Me acabo de dar cuenta de que el OP estaba hablando de mutexes soportados por OS. En los ejemplos que he examinado, los mutex no inflados se implementaron directamente utilizando instrucciones CAS y similares, en lugar de usar las funciones de la biblioteca pthread, etc.
Nunca puede estar seguro de que un objeto nunca se utilizará como bloqueo (considere la reflexión). Normalmente, cada objeto tiene un encabezado con algunos bits dedicados al bloqueo. Es posible implementarlo de tal manera que el encabezado solo se agregue cuando sea necesario, pero eso se complica un poco y probablemente necesite algún encabezado de todos modos (clase (equivalente a "vtbl" y tamaño de asignación en C ++), código hash y recolección de basura) .
Aquí hay una página wiki sobre la implementación de la sincronización en OpenJDK.
(En mi opinión, agregar un bloqueo a cada objeto fue un error).