2017-02-27 115 views
2

所以我写了一个单元测试来测试一些多线程,并且我想知道这个代码是否能够保证按照我的预期工作。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) 
    } 
} 

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

(我加了Java作为一种标记,因为大概的基本问题是Java中的相同。)

回答

10

当与Kotlin 1.1 RC编译器编译,在你的代码中的局部变量都存储在ObjectRef S,然后将其用于在lambda中。

你可以使用Kotlin bytecode viewer来检查一段代码是否被编译。

ObjectRef存储在非易失性领域参考,所以确实不能保证程序完成。

科特林的早期版本使用有在Refvolatile场,但是这是一个未公开的实现细节,在科特林1.1 has eventually been changed(即不是靠)。有关非易失性捕获变量背后的动机,请参见this thread


the issue描述说,

如果用户被捕获的变量和递给其他线程的工作,那么它是任何并发控制机构的需求,他们正在使用,以在读取和写入捕获的变量之间建立相应的发生之前的边缘。所有常规的并发机制,例如开始/加入线程,创建期货等都这样做。

为了使您的示例程序正确同步,这是足以称之为从exec.submit { }返回两个Future实例.get(),因为Future提供的之前发生担保:异步计算采取

操作代表通过在另一个线程中通过Future.get()检索结果之后发生的动作之前执行Future

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)