有一点需要考虑的是,当两个不同的线程调用同一个函数或与同一个对象交互时,应用程序的“状态”可能会发生改变。在这种情况下,你的“状态”可能是“什么被写入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(即上面的场景)。
作为反点,这里是一个非常thread- 未安全的方法:
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倾向于选择“不变性”和“无状态性”的原因之一,因为那些工具根本不需要担心这种问题。
如果您发现自己被迫处理可变状态,通常确保线程安全的最简单方法是确保您的方法一次只能调用一次,即使用。在更细粒度的级别上,您希望确保特定的步骤序列不与另一个线程上的该序列的另一个副本交错(例如,我在开始描述的GetDetails场景)。
你也可以看看semaphores但这超出了这个答案的范围。
只要输入不同(并且没有连接到下面的同一个流),就没问题。 – Dima