Seguridad de rosca variable local de Kotlin

Así que estaba escribiendo una testing unitaria para probar algunos subprocesss múltiples, y quiero saber si este código está garantizado para funcionar como yo esperaba.

fun testNumbers() { var firstNumber: Int? = null var secondNumber: Int? = null val startLatch = CountDownLatch(2) val exec = Executors.newFixedThreadPool(2) exec.submit({ startLatch.countDown() startLatch.await() firstNumber = StuffDoer.makeNumber() }) exec.submit({ startLatch.countDown() startLatch.await() secondNumber = StuffDoer().makeNumber() }) while (firstNumber == null || secondNumber == null) { Thread.sleep(1) } } 

Específicamente, ¿este método está garantizado para completarse? firstNumber y secondNumber no son volatile ¿eso significa que los resultados establecidos en esos valores de los hilos del exec podrían no ser vistos por el hilo que ejecuta la testing? No puede aplicar variables volátiles a locales, por lo que, en términos prácticos, no tendría sentido para mí que no pueda hacer que las variables locales de la function sean volátiles si fuera necesario.

(Agregué Java como una label porque presumiblemente la pregunta básica es la misma en Java).

Cuando se comstack con el comstackdor Kotlin 1.1 RC , las variables locales en su código se almacenan en ObjectRef s, que luego se utilizan en las lambdas.

Puede verificar a qué parte del código se comstack utilizando el visor de códigos de bytes Kotlin .

ObjectRef almacena la reference en un campo no volátil , por lo que no hay garantía de que el progtwig finalice.

Las versiones anteriores de Kotlin solían tener un campo volatile en las classs Ref , pero esto era un detalle de implementación no documentado (es decir, no era algo en lo que confiar) que finalmente se modificó en Kotlin 1.1. Vea este hilo para la motivación detrás de las variables capturadas no volátiles.


Como se dijo en la descripción del problema ,

Si un usuario está capturando una variable y entregándola a otros hilos para que trabaje con ella, entonces es un requisito de cualquier mecanismo de control de concurrency que esté utilizando para establecer los bordes correspondientes de anterioridad entre lecturas y escrituras en las variables capturadas. Todos los mecanismos de concurrency regulares como iniciar / unir hilos, crear futuros, etc. lo hacen.

Para hacer que su progtwig de ejemplo esté correctamente sincronizado, es suficiente llamar a .get() en las dos instancias Future devueltas desde exec.submit { } , ya que Future proporciona sucede antes de las garantías:

Las acciones tomadas por el cálculo asynchronous representado por un Future suceden antes de las acciones posteriores a la recuperación del resultado a través de Future.get() en otro hilo.

 val f1 = exec.submit { /* ... */ } val f2 = exec.submit { /* ... */ } f1.get() f2.get() // Both assignments made in the submitted tasks are visible now assert(firstNumber != null) assert(secondNumber != null)