Supongamos que tengo un campo al que se accede simultáneamente y se lee muchas veces y rara vez se escribe.
public Object myRef = new Object();
Digamos que un subproceso T1 establecerá myRef en otro valor, una vez por minuto, mientras que otros subprocesos leerán myRef miles de millones de veces de forma continua y simultánea. Solo necesito que myRef sea finalmente visible para todos los hilos.
Una solución simple sería usar una referencia atómica o simplemente volátil como este:
public volatile Object myRef = new Object();
Sin embargo, las lecturas volátiles afaik incurren en un costo de rendimiento. Sé que es minúsculo, esto se parece más a algo que me pregunto que a algo que realmente necesito. Así que no nos preocupemos por el rendimiento y asumamos que esta es una pregunta puramente teórica.
Entonces, la pregunta se reduce a: ¿Hay alguna forma de evitar con seguridad las lecturas volátiles para referencias que rara vez se escriben, haciendo algo en el sitio de escritura?
Después de leer un poco, parece que las barreras de memoria podrían ser lo que necesito. Entonces, si existiera una construcción como esta, mi problema se resolvería:
- Escribir
- Invocar barrera (sincronización)
- Todo está sincronizado y todos los hilos verán el nuevo valor. (sin un costo permanente en los sitios de lectura, puede ser obsoleto o incurrir en un costo único a medida que se sincronizan los cachés, pero después de eso todo vuelve al campo normal hasta la próxima escritura).
¿Existe tal construcción en Java, o en general? En este punto no puedo evitar pensar que si existiera algo así, ya habría sido incorporado a los paquetes atómicos por las personas mucho más inteligentes que los mantienen. (¿La lectura vs escritura desproporcionadamente frecuente podría no haber sido un caso para cuidar?) Entonces, ¿tal vez hay algo mal en mi pensamiento y tal construcción no es posible en absoluto?
He visto que algunos ejemplos de código usan 'volátil' para un propósito similar, explotando su contrato anterior. Hay un campo de sincronización separado, por ejemplo:
public Object myRef = new Object();
public volatile int sync = 0;
y al escribir hilo / sitio:
myRef = new Object();
sync += 1 //volatile write to emulate barrier
No estoy seguro de que esto funcione, y algunos argumentan que esto funciona solo en la arquitectura x86. Después de leer secciones relacionadas en JMS, creo que solo está garantizado que funcione si esa escritura volátil se combina con una lectura volátil de los hilos que necesitan ver el nuevo valor de myRef. (Entonces no se deshace de la lectura volátil).
Volviendo a mi pregunta original; ¿Es esto posible? ¿Es posible en Java? ¿Es posible en una de las nuevas API en Java 9 VarHandles?
sync += 1;
y los hilos de su lector leen el sync
valor, también verán la myRef
actualización. Debido a que solo necesita que los lectores vean la actualización eventualmente , podría usar esto para su ventaja para leer solo la sincronización en cada 1000 iteración del hilo del lector, o algo similar. Pero también puede hacer un truco similar volatile
: simplemente guarde myRef
en caché el campo en los lectores durante 1000 iteraciones, luego
sync
campo, no, los lectores no tocarían el sync
campo en cada iteración, lo harían de manera oportunista, cuando quieran verificar si ha habido una actualización. Dicho esto, una solución más simple sería almacenar en caché myRef
durante 1000 rondas, luego volver a leerlo ...