You have come here that means you know about Threads and concurrency. If you have doubts regarding this then you can read the blog on concurrency. As Thread and Runnable have been there for long as the very first generation of concurrent execution approaches in Scala. Creating a new Thread takes less computational time compared to creating a new JVM process. we cannot afford to create a fresh Thread for each of these tasks because if an application performs a large number of small concurrent tasks then it requires high throughput. Now let’s discuss Executor and Execution Context objects.

Thread Pools
Starting a thread required us to allocate a memory region for its call stack and context switch from one Thread to another. Which consumes much more time than work in the concurrent task. For this reason, most concurrency frameworks have facilities that maintain a set of Threads in a waiting state and start running when concurrently executable work tasks become available. Generally, we call such facilities thread pools.
To allow programmers to encapsulate the decision of how to run concurrently executable work tasks, JDK comes with an abstraction called Executor. The executor is an interface and in this, a single execute method is defined. This method takes a runnable object and calls its run method.
ForkJoinPool
It is an Executor which is introduced in JDK7. Its threads are daemons by default, which means there is no need to shut it down explicitly at the end of the program. Scala programmers can use it in JDK6 by importing scala.concurrent.forkjoin package. Let’s see the implementation and submit tasks that can be asynchronously executed. Read more about Fork/Join.
import scala.concurrent._
import java.util.concurrent.ForkJoinPool
object ExecutorsCreate extends App {
val executor = new ForkJoinPool
executor.execute(new Runnable {
def run() = log("This task is run asynchronously.")
})
Thread.sleep(1000)
}
In this code, Firstly we import the package and instantiate ForkJoinPool class and assign it to the executor. Now, this executor sent the task in the form of a runnable object that prints to the standard output. Finally, we use Thread.sleep to prevent the daemon threads in the ForkJoinPool instance from being terminated.
ExecutorService
It is the subtype of Executor interface which also implemented by the ForkJoinPool class. ExecutorService extends Executor which defines convenience methods. In this, I will talk about the shutdown method, this method makes sure that Executor object terminates by all executing all the submitted tasks and then stopping all the worker threads. When your program no longer needs the ExecutorService object you created, you should ensure that the shutdown method is called.
In the above example we used the sleep method to prevent from this we can use the awaitTermination method. This method specifying the maximum amount of time to wait for their completion. Let’s see the example (this code is continues of above code).
import java.util.concurrent.TimeUnit
executor.shutdown()
executor.awaitTermination(60, TimeUnit.SECONDS)
Execution Context
If you are a Scala programmer then scala.concurrent package defines the ExecutionContext trait that offers similar functionality to that of Executor objects. Many scala methods take ExecutionContext objects as implicit parameters. It implements the abstract execute method, which exactly corresponds to the execute method on the Executor interface, and the reportFailure method. The ExecutorContext companion object contains the default execution called global, which internally uses a ForkJoinPool instance. By this, we can pass parameter or by importing the package.
Let’s see an example.
object ExecutionContextGlobal extends App {
val ectx = ExecutionContext.global
ectx.execute(new Runnable {
def run() = log("Running on the execution context.")
})
Thread.sleep(500)
}
In this Example, we instance the global to ectx and then send the task in the form of a Runnable object.
By importing package:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val fut = Future { Thread.sleep(10000); 21 + 21 }
In this example, we import global to globally which means we don’t need to instance global again and again.
Thank you for sticking to the end. If you like this blog, please do show your appreciation by giving thumbs ups and share this blog and give me a suggestion on how I can improve my future posts to suit your needs. Follow me to get updates on different technologies. For any queries, feel free to contact me at jashan.goyal@knoldus.in .