我目前使用Akka actor来建立一个以线程安全方式原子执行的代码块(Akka邮箱语义通过一次处理一条消息来强制原子性)。
然而,这引入了对actor系统的需求,以及额外的副作用或膨胀(必须手动将异常传播到调用者,失去ask
上的类型安全性,并且通常使用消息语义而不是函数调用)。
线程安全的原子代码块可以用一种更简单的方式在scala中实现吗?你会把@volatile
应用于函数吗?
这取决于您想要保护的共享状态类型:
-
最简单和通用的选择是使用相同的旧
synchronized
。然而,与Akka不同的是,它是完全阻塞的,因此可能很容易扼杀你的性能,当然还有代码风格,因为很难控制混乱的副作用。它还可以允许死锁。 -
Java的锁是相同的方法,但可能会在性能上稍微好一点。
-
另一个选项是相同的旧Java的
AtomicReference
(实现CAS操作)和相关的类。积极的一面是它们是非阻塞的——开发人员实际上使用它们来构建高性能的集合。这里描述了使用锁和CAS的方法。它们都是非常低级的机制,所以我不建议过多使用它们,尤其是对于业务逻辑(任何参与者的实现都会更好)。 -
若您的共享状态是一个集合,那个么您可能希望使用相同的旧Java的并发集合(它们具有类似
putIfAbscent
的原子操作)。例如,Scala有有趣的非阻塞TrieMap。 -
Scala STM也是一种替代
-
最后,这个问题专门讨论轻量级actor模型实现。
附言:Volatile注释不过是Java中的volatile
关键字模拟。你可以把它放在方法上,因为任何注释都可以放在任何东西上。
根据您想要实现的目标,最简单的可能是旧的synchronized
:
//your mutable state
private var x = 0
//better than locking on 'this' is to have a dedicated lock
private val lock = new Object
def add(i:Int) = lock.synchronized { x += i }
这是"旧Java"方式,但它可能对您有效,这取决于您正在做什么。当然,如果同步操作更复杂和/或需要高吞吐量,这是死锁的最快方法。