Delegating the management of threads to executors

Before Java 5, the Java Concurrency API, when we wanted to implement a concurrent application, we had to manage the threads by ourselves. First we used to implement the Runnable interface or an extension of the Thread class. Then, we used to create a thread object and start its execution using its start() method. We also had to control its status to know whether the thread had finished its execution or was still running.

In Java version 5, the concept of executor as a provider of a pool of execution threads appeared. This mechanism, implemented by the Executor and ExecutorService interfaces and the ThreadPoolExecutor and ScheduledThreadPoolExecutor classes, allows you to concentrate only on the implementation of the logic of the task. You implement the task and send it to the executor. It has a pool of threads, and it is this pool that is responsible for the creation, management, and finalization of the threads. In Java version 7, another implementation of the executor mechanism in the fork/join framework, specialized in problems that can be broken down into smaller subproblems, appeared. This approach has numerous advantages, which are as follows:

  • We don't have to create threads for all the tasks. When we send a task to the executor and it's executed by a thread of the pool, we save the time used in creating a new thread. If our application has to execute a lot of tasks, the total saved time will be significant and the performance of the application will be better.
  • If we create fewer threads, our application will use less memory as well. This can also extract better performance from our application.
  • We can build concurrent tasks executed in the executor by implementing either the Runnable or Callable interface. The Callable interface allows us to implement tasks that return a result, which provide a big advantage over traditional tasks.
  • When we send a task to an executor, it returns a Future object that allows us to know the status of the task and the returned result, whether it has finished its execution easily.
  • We can schedule our tasks and execute them repeatedly with the special executor implemented by the ScheduledThreadPoolExecutor class.
  • We can easily control the resources used by an executor. We can establish the maximum number of threads in the pool, so our executor will never have more than that number of tasks running at a time.

The use of executors has a lot of advantages over direct utilization of threads. In this recipe, you are going to implement an example that shows how you can obtain better performance using an executor than creating the threads yourself.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.37.250