I was told that a problem of Future is such that if i await for the result and it times out. The future actually still runs in the background somewhere since Future is not cancellable.
I am trying to prove to myself this theory
object Main extends App {
import concurrent.duration._
// single threaded execution context
implicit val context = ExecutionContext.fromExecutor(Executors.newSingleThreadExecutor())
val f = Future {
def infinite(n: Int): Int = {
infinite(n + 1)
}
//infinite(0)
1
}
println(Await.result(f, 3.seconds))
println("Finished Await")
}
I expect that if i run infinite in the future. The main thread will timeout and the Future continues running. Thatâs what happens, true but if i return the future instantly the main thread still blocks ??? Can someone point out to me what i am doing wrong?
The reason is that threads in thread pools are by default non-daemon threads (so they block exiting from VM) and you donât explicitly shut them down.
There are some solutions.
Not recommended solution - you can make a thread pool out of daemon threads. They wonât block exiting from VM:
// don't use this solution!!!
implicit val context: ExecutionContextExecutor = {
val defaultThreadFactory = Executors.defaultThreadFactory()
val threadFactory: ThreadFactory = runnable => {
val thread = defaultThreadFactory.newThread(runnable)
thread.setDaemon(true)
thread
}
val executorService = Executors.newSingleThreadExecutor(threadFactory)
ExecutionContext.fromExecutor(executorService)
}
You can explicitly shutdown thread pool (that works only if you donât have running infinite jobs):
implicit val context: ExecutionContextExecutorService = {
val executorService = Executors.newSingleThreadExecutor()
ExecutionContext.fromExecutorService(executorService)
}
val f = Future { 1 }
try {
println(Await.result(f, 3.seconds))
} finally {
println("Finished Await")
context.shutdown()
}
Also not generally recommended (i.e. useful, but rather not as something scattered all over your codebase): invoke System.exit - that will exit no matter what:
implicit val context: ExecutionContextExecutorService = {
val executorService = Executors.newSingleThreadExecutor()
ExecutionContext.fromExecutorService(executorService)
}
val f = Future { 1 }
try {
println(Await.result(f, 3.seconds))
} finally {
println("Finished Await")
System.exit(0)
}
FWIW, if I run your code as it is, it prints 1 followed by âFinished Awaitâ and then keeps running until it prints: âtimed out after 30 seconds when running codeâ
if I get hold of the executor from which you obtain a context, and tell the executor to shutdown, then the program prints 1 followed by âFinished Awaitâ and then completes.
object Main extends App {
import java.util.concurrent.{Executors, ExecutorService}
import concurrent.duration._
import scala.concurrent.ExecutionContext
import scala.concurrent.{Future,Await}
val executor = Executors.newSingleThreadExecutor()
// single threaded execution context
implicit val context = ExecutionContext.fromExecutor(executor)
val f = Future {
def infinite(n: Int): Int = {
infinite(n + 1)
}
//infinite(0)
1
}
println("requesting executor shutdown")
executor.shutdown
println(Await.result(f, 3.seconds))
println("Finished Await")
}
Thank you for the suggestions. It seems like the theory is true. The future still runs even when Await has timed out.
I realised that i have such poor understanding of Java concurrency. Is Scala Future using java concurrency libraries under the hood? How about libraries like cats-effect or ZIO or Monix? Do they have their own implementation of concurrency from the ground up?
If i want to understand more about all this should i pick up a book on Java concurrency? Because if libraries like cats-effect or ZIO or Monix have their own implementation from the ground up then what i will learn from Java concurrency might not be so relevant.
object Main extends App {
import concurrent.duration._
// single threaded execution context
implicit val context: ExecutionContextExecutorService = {
val executorService = Executors.newSingleThreadExecutor()
ExecutionContext.fromExecutorService(executorService)
}
val f = Future {
Thread.sleep(10.seconds.toMillis)
println("Finish sleeping")
}
try {
println(Await.result(f, 3.seconds))
} catch {
case _: Exception => println("Caught exception")
}
finally
{
println("Finished Await")
context.shutdown()
}
println("At main")
}
Output
Caught exception
Finished Await
At main
//after 10 seconds
Finish sleeping
Failure(java.util.concurrent.TimeoutException: Future timed out after [1 second])
Failure(java.util.concurrent.TimeoutException: Future timed out after [1 second])
Success(())
Success(())
Success(())
They all use at least Java Threads under the hood (plus Executors, but I donât know all the details). It depends on what you mean by âfrom the ground upâ, but in general when you run code on Java platform you want to use some Java standard library functions in the end. What would be the alternative? Coding your own standard library and replacing the Javaâs one? Well, that doesnât make much sense.
Yes i believe ultimately they all will use java threads under the hood. I was just thinking do they use javaâs implementation of the thread pool or do they have their own implementation and custom strategies. At which level of abstraction can we say its Java and from there on its Scala
Well it is true at the end all those technologies will be running on JVM threads, they provide sufficient abstraction so you do not need to worry about that (expect for configuring your thread pools which is really important).
For example things like fibers, async and concurrent combinators, etc.
So, it really depends what you mean with learning Java concurrency.
Java concurrency per se is only semi-relevant â this isnât Java. JVM concurrency, though, is enormously relevant, though: the JVM is the platform that both Java and Scala (usually) run on. So itâs worth at least basically understanding the JVMâs concept of threads, and that tends to be explained in terms of Java.
That said â Scala Futures are a much higher-level construct than JVM threads, so donât make yourself crazy learning the Java side of it, because much of it is irrelevant. It might be worth reading the first few chapters of a Java book â the explanations of what threads are and how they work â but donât waste time on most of it. (I suspect you could find some perfectly adequate articles online, but I havenât really looked around for this.)
Also, note that much of this involved the underlying hardware: threading is really a pretty low-level construct, usually with hardware support.
The FP libraries â cats-effect, Monix and ZIO â all use a yet higher-level construct, typically called fibres. They arenât building concurrency from the ground up, but they apply their own abstraction on top of that low-level concurrency. Deep under the hood there are still threads there, but you donât interact with them directly nearly as much in the FP world: the majority of your code can ignore threads except when external constraints require you to deal with them. The FP engine itself will manage the relationship between your code and the threads to run it on.