Someone asked an interesting question this past week: What’s the difference between a Scala Future
and a Thread
?
At first I thought the answer was pretty obvious, but as I tried to explain it, I realized that I needed to consider the differences more before speaking. After some thinking, I finally decided that the differences are in their APIs, and as a result of that, significant differences in how they are used.
If you’re just interested in my summary/conclusions, jump down to the Summary section. Otherwise, read on.
Warning: This article is a work-in-progress. I need to put much more research into it.
Scala/Java Thread API
Technically there is no such thing as a “Scala Thread.” If you type this in your Scala IDE:
val t = new Thread
you’ll see that a Thread
is really a java.lang.Thread. Putting that technicality behind us, some of the most important parts of the Java Thread
API looks like this:
Constructors: Thread() Thread(Runnable target) Methods: interrupt join run start yield
Here’s an example of how you might use a Thread
in Scala:
val thread = new Thread { override def run { // put your long-running code here ... } } thread.start
As the API and code shows, a Thread
is a general-purpose concurrency abstract. While you may not notice it at this point, it’s important to note that the Thread
has no return type.
The Thread Javadoc gives this description of a Thread
:
A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
Scala Future
The Scala Future
is well-described on the official Futures and Promises page:
Futures provide a nice way to reason about performing many operations in parallel -- in an efficient and non-blocking way. The idea is simple; a Future is a sort of a placeholder object that you can create for a result that does not yet exist. Generally, the result of the Future is computed concurrently and can be later collected. Composing concurrent tasks in this way tends to result in faster, asynchronous, non-blocking parallel code.
I read somewhere else that a Future
“represents the result of an asynchronous computation,” and I like that description.
They also add this:
A Future is an object holding a value which may become available at some point. (My emphasis.)
This value is usually the result of some other computation: (1) If the computation has not yet completed, we say that the Future is not completed. (2) If the computation has completed with a value or with an exception, we say that the Future is completed.
The Future
API is significantly different from a Java Thread
. I provide a Future example here that shows you can create a Future
like this:
val f = Future { // your long-running task here that returns an Int ... }
Note that a Future
has a type, so I could have written that code like this to be more explicit:
val f: Future[Int] = Future { // your long-running task here that returns an Int ... }
A common way to use a Future
is to use its callback methods. You can do this in a variety of ways. One way is with the onComplete
method, where you handle whether the Future
returned successfully, or not:
f.onComplete { case Success(value) => println(s"Got the callback, meaning = $value") case Failure(e) => e.printStackTrace }
You also also use the onSuccess
and onFailure
callback methods, like this:
f onSuccess { case 0 => println("got a zero") }
and this:
f onFailure { case t => println("D'oh! Got an error: " + t.getMessage) }
In some cases it can be inconvenient to use these methods, so the Scala Future
“provides combinators which allow a more straightforward composition.” The “Futures and Promises” page shows this example of how to use a map
method with a Future
:
val rateQuote = future { connection.getCurrentValue(USD) } val purchase = rateQuote map { quote => if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("not profitable") } purchase onSuccess { case _ => println("Purchased " + amount + " USD") }
I’m not going to go into the details of this example, I just want to show another way the API is different. Please see the Futures and Promises tutorial to read more about this example.
To summarize the Scala Future
, its API looks like this:
Creating a Future: val f = Future { // your long-running code here ... } Some of the methods available on a Future: isCompleted onComplete ready result value andThen collect filter flatMap foreach map onFailure onSuccess recover recoverWith zip
Finally, I think this text from the “Futures and Promises” page is also important:
We should now comment on when exactly the callback gets called. Since it requires the value in the future to be available, it can only be called after the future is completed. However, there is no guarantee it will be called by the thread that completed the future or the thread which created the callback. Instead, the callback is executed by some thread, at some time after the future object is completed. We say that the callback is executed eventually.
So, when you manually use a Thread
, you wait for that one thread to return, but when you use a Future
, there’s no guarantee that the callback will be called on the same thread that your Future
ran on.
The Future as a ConcurrentTask
As I wrote in this article, when you first start working with a Future
, it may help to think of it as a “concurrent task.” I demonstrated that in this code:
package futures import scala.concurrent.{Future => ConcurrentTask} // rename import scala.concurrent.ExecutionContext.Implicits.global import scala.util.{Failure, Success} object FutureAsConcurrentTask extends App { // run some long-running task (task has type Future[Int] in this example) val task = ConcurrentTask { Cloud.executeLongRunningTask } // whenever the task completes, execute this code task.onComplete { case Success(value) => println(s"Got the callback, value = $value") case Failure(e) => println(s"D'oh! The task failed: ${e.getMessage}") } }
In that code I renamed the Future
class to ConcurrentTask
when I imported it. At the time I wrote that article, that way of thinking helped me understand Scala futures.
Summary: Thread vs Future
I know that I need to write more about this and clarify my points, but as a summary for today:
- A Java
Thread
represents a thread of execution in an Java/Scala/JVM application. - You can run code in parallel in a
Thread
, but aThread
does not have a return type. - A Scala
Future
represents the result of an asynchronous computation, and has a return type. - A Scala
Future
works with callback methods. - A Scala
Future
can be composed, and has usual collection methods likemap
,flatMap
,filter
, etc. - There is no guarantee that your
Future
’s callback method will be called on the same thread the future was run on.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Notes to self
As a “note to self,” I need to research and discuss how a thread works with the JVM’s shared memory model, and how the Scala Future
compares to that.