Concurrency Primitives in Scala
You are right about the "Akka orientation" in Scala: all in all I think there is a pretty overlap of Scala Language developer community and Akka ones. Late versions of Scala relies on Akka actors for concurrency, but frankly I cannot see anithing bad in this.
Regarding your question, to cite "Akka in Action" book:
Actors are great for processing many messages, capturing state and reacting with different behaviors based on the messages they receive"
and
"Futures are the tool to use when you would rather use functions and don't really need objects to do the job"
A Future is a placeholder for a value not yet available, so even if it is used for non blocking tasks, it's useful for more than that and I think it could be your choice here.
You should be aware that your implementation of FooCounter
is not thread-safe. If multiple threads simultaneously invoke the get
, set
and inc
methods, there is no guarantee that the count will be accurate. You should use one of the following utilities to implement a correct concurrent counter:
synchronized
statement (Scalasynchronized
statement is similar to the one in Java)- atomic variables
- STMs (for example, ScalaSTM)
- potentially, you could use an Akka actor that is a counter, but note that this is not the most straightforward application of actors
Other Scala concurrency utilities, such as futures and promises, parallel collections or reactive extensions, are not the best fit for implementing a concurrent counter, and have different usages.
You should know that Scala in some cases reuses the Java concurrency infrastructure. For example - Scala does not provide atomic variables of its own, since Java atomic classes already do the job. Instead, Scala aims to provide higher-level concurrency abstractions, such as actors, STMs and asynchronous event streams.
Benchmarking
To assess the running time and efficiency of your implementation, a good choice is ScalaMeter. The online documentation at the website contains detailed examples on how to do benchmarking.
Documentation and concurrency libraries
While Akka is the most popular and best documented Scala concurrency utility, there are many other high quality implementations, some of which are more appropriate for different tasks:
- futures and promises
- parallel collections and ScalaBlitz
- software transactional memory
- reactive extensions
I think Learning Concurrent Programming in Scala might be a good book for you. It contains detailed documentation of different concurrency styles in Scala, as well as directions on when to use which. Chapters 5 and 9 also deal with benchmarking. Specifically, a concurrent counter utility is described, optimized, and made scalable in the Chapter 9 of the book.
Disclaimer: I'm the author.
If you're not willing to use java.util.concurrent
directly, then your most elegant solution may be using Akka Agents.
import scala.concurrent.ExecutionContext.Implicits.global
import akka.agent.Agent
class FooCounter {
val counter = Agent(0)
def get() = counter()
def set(v: Int) = counter.send(v)
def inc() = counter.send(_ + 1)
def modify(f: Int => Int) = counter.send(f(_))
}
This is asynchronous, and guarantees sequentiality of operations. The semantics are nice, and if you need more performance, you can always change it to the good old java.util.concurrent
analogue:
import java.util.concurrent.atomic.AtomicInteger
class FooCounter {
val counter = new AtomicInteger(0)
def get() = counter.get()
def set(v: Int) = counter.set(v)
def inc() = counter.incrementAndGet()
def modify(f: Int => Int) = {
var done = false
var oldVal: Int = 0
while (!done) {
oldVal = counter.get()
done = counter.compareAndSet(oldVal, f(oldVal))
}
}
}
You can use all Java syntonization primitives, like AtomicInteger for example for counting.
For more complicated tasks I personally like scala-stm library: http://nbronson.github.io/scala-stm/
With STM your example will look like this
object FooCounter{
private val counter = Ref(0);
def get() = atomic { implicit txn => counter() }
def set(value: Int) = atomic { implicit txn => counter() = counter() + value }
def inc() = atomic { implicit txn => counter() = counter() + 1 }
}
However for this simple example I would stop on Java primitives.