我目前使用Akka actor来建立一个以原子方式和线程安全方式执行的代码块(Akka邮箱语义通过一次处理一条消息来强加原子性)。
然而,这引入了对actor系统的需求,以及额外的副作用或膨胀(必须手动将异常传播给调用者,在ask
上失去类型安全性,并且通常使用消息语义而不是函数调用)。
可以用更简单的方式在scala中完成线程安全的原子代码块吗?你会将@volatile
应用于函数吗?
答案 0 :(得分:4)
这取决于您希望在此处保护的共享状态:
最简单和最普遍的选择是使用相同的旧synchronized
。然而,与Akka不同,它完全阻挡,因此很容易扼杀你的表现,当然还有代码风格,因为很难控制混乱的副作用。它也可能允许dead-locks。
Java locks采用相同的方法,但性能可能稍好一些。
另一个选项是相同的旧Java AtomicReference
(实现CAS操作)和相关类。积极的一点是它们是非阻塞的 - 开发人员实际上使用它们来构建高性能的集合。使用锁和CAS的方式被描述为here。它们都是相当低级别的机制,因此我不建议使用它们,特别是对于业务逻辑(任何演员的实现都会更好)。
如果您的共享状态是一个集合 - 您可能希望使用相同的旧Java并发集合(它们具有putIfAbscent
等原子操作)。例如,Scala有一些有趣的非阻塞TrieMap。
Scala STM也是另类
最后,this question专门用于轻量级演员模型实现。
P.S。易失性注释只不过是来自Java的volatile
关键字模拟。你可以把它放在方法上,因为任何注释都可以放在任何东西上。
答案 1 :(得分:2)
根据您要实现的目标,最简单的可能是旧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'的方式,但它可能对您有用,具体取决于您正在做什么。当然,如果您的同步操作更复杂和/或您需要高吞吐量,这是解决死锁的最快方法。