Kotlin局部可变螺纹安全

Cor*_*han 3 multithreading kotlin

所以我正在编写一个单元测试来测试一些多线程,我想知道这段代码是否能保证按照我的预期工作.

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

具体来说,这种方法是否可以保证完成?firstNumber并且secondNumber不是volatile这样意味着exec运行测试的线程可能永远不会看到线程中那些值设置的结果?你不能将volatile应用于局部变量,所以实际上对我来说,如果可能有必要,你不能使函数局部变量变得不稳定.

(我将Java添加为标记,因为大概在Java中基本问题是相同的.)

hot*_*key 10

使用Kotlin 1.1 RC编译器编译时,代码中的局部变量存储在ObjectRefs中,然后在lambdas中使用.

您可以使用Kotlin字节码查看器检查编译代码的内容.

ObjectRef将引用存储在非易失性字段中,因此确实无法保证程序完成.

早期版本的Kotlin曾经volatileRef类中有一个字段,但这是一个未记录的实现细节(即不依赖于它),最终在Kotlin 1.1中有所改变.请参阅此主题以了解非易失性捕获变量背后的动机.


问题描述中所述,

如果用户正在捕获变量并将其交给其他线程进行操作,则需要使用它们所使用的任何并发控制机制来建立读取和写入捕获变量之间的相应发生前沿.所有常规并发机制,如启动/加入线程,创建期货等都是这样做的.

为了使您的示例程序正确同步,调用返回.get()的两个Future实例就足够了exec.submit { },因为Future提供了before-before保证:

异步计算所采取的动作由在另一个线程中Future检索结果之后的发生前动作表示Future.get().

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) 
Run Code Online (Sandbox Code Playgroud)