object GetDetails{
def apply(outStream: java.io.Writer, someObject: SomeClass){
outStream.write(someObject.detail1) //detail1 is string
outStream.write(someObject.detail2) //detail2 is also string
outStream.flush()
}
}
如果此实现不是线程安全的,我该如何使其成为线程安全的?
将使用不同的输入同时调用此函数。
答案 0 :(得分:0)
要考虑的一件事是当两个不同的线程调用相同的函数或与同一个对象交互时,应用程序的“状态”可能会如何变化。在这种情况下,您的“状态”可能是“已写入outStream
”的内容。
考虑以下情况:
Thread 1 Thread 2
-------------------- -------------------------------
GetDetails(outStream, object1)
GetDetails(outStream, object2)
outStream.write(object1.detail1)
outStream.write(object2.detail1)
outStream.write(object1.detail2)
outStream.write(object2.detail2)
outStream.flush()
outStream.flush()
两个单独的主题调用GetDetails
,共享相同的outStream
。这是一个潜在的并发问题,因为写入outStream
的数据不能保证按任何特定顺序排列。您可能会获得[object1.detail1, object2.detail1, object1.detail2, object2.detail2]
或[object2.detail1, object1.detail1, object1.detail2, object2.detail2]
,依此类推。
GetDetails.apply
不会更改任何GetDetails
的状态,但它确实会更改您通过的Writer
的状态;为了确保线程安全,您必须努力避免同时使用相同的Writer(即上面的场景)。
作为一个反点,这是一个非常好的线程 - un 安全方法:
object NotThreadSafe {
// mutable state
private var currentPrefix = ""
def countUp(prefix: String) = {
// red flag: changing mutable state, then referring to it
currentPrefix = prefix
for(i <- 1 to 5) println(s"$currentPrefix $i")
}
}
如果线程1调用NotThreadSafe.countUp("hello")
而线程2调用NotThreadSafe.countUp("goodbye")
,则输出将取决于最后发生的currentPrefix = prefix
。
你最终可以
你好1
你好2
再见3 //应该是你好3,但currentPrefix改变了 再见1
再见4 //应该是你好4 再见5 //应该是你好5 再见2
再见3
再见4
再见5
或许多其他排列中的一种。
这就是Scala倾向于选择“不变性”和“无国籍”的原因之一,因为这些工具根本不需要担心这类问题。
如果您发现自己被迫处理可变状态,通常最简单的方法是确保您的方法一次只能被调用一次,即使用synchronized
。在更精细的级别,您希望确保特定的步骤序列不与另一个线程上的该序列的另一个副本交错(例如,我在开头描述的GetDetails场景)。
您也可以查看semaphores,但这超出了此答案的范围。