- Mastering Concurrency Programming with Java 8
- Javier Fernández González
- 688字
- 2021-07-16 12:55:02
An introduction to executors
The basic mechanism to implement a concurrent application in Java is:
- A class that implements the Runnable interface: This is the code you want to implement in a concurrent way
- An instance of the Thread class: This is the thread that is going to execute the code in a concurrent way
With this approach, you're responsible for creating and manning the Thread
objects and implementing the mechanisms of synchronization between the threads. However, it can have some problems, especially with those applications with a lot of concurrent tasks. If you create too many threads, you can degrade the performance of your application or even hang the entire system.
Java 5 included the executor framework, to solve these problems and provide an efficient solution, which would be easier for the programmers to use than the traditional concurrency mechanisms.
In this chapter, we will introduce the basic characteristics of the executor framework by implementing the following two examples using that framework:
- The k-nearest neighbors algorithm: This is a basic machine-learning algorithm used in classification. It determines the tag of a test example based on the tag of the k most similar examples in the train dataset.
- Concurrency in a client/server environment: Applications that serve information to thousands or millions of clients are critical nowadays. It is essential to implement the server side of the system in an optimal way.
In Chapter 3, Getting the Maximum from Executors, and Chapter 4, Getting Data from the Tasks – The Callable and Future Interfaces, we will introduce more advanced aspects of executors.
Basic characteristics of executors
The main characteristics of executors are:
- You don't need to create any
Thread
object. If you want to execute a concurrent task, you only create an instance of the task (for example, a class that implements theRunnable
interface) and send it to the executor. It will manage the thread that will execute the task. - Executors reduce the overhead introduced by thread creation reusing the threads. Internally, it manages a pool of threads named worker-threads. If you send a task to the executor and a worker-thread is idle, the executor uses that thread to execute the task.
- It's easy to control the resources used by the executor. You can limit the maximum number of worker-threads of your executor. If you send more tasks than worker-threads, the executor stores them in a queue. When a worker-thread finishes the execution of a task, it takes another from the queue.
- You have to finish the execution of an executor explicitly. You have to indicate to the executor that it has to finish its execution and kill the created threads. If you don't do this, it won't finish its execution and your application won't end.
Executors have more interesting characteristics that make them very powerful and flexible.
Basic components of the executor framework
The executor framework has various interfaces and classes that implement all the functionality provided by executors. The basic components of the framework are:
- The Executor interface: This is the basic interface of the executor framework. It only defines a method that allows the programmer to send a
Runnable
object to an executor. - The ExecutorService interface: This interface extends the
Executor
interface and includes more methods to increase the functionality of the framework, such as the following:- Execute tasks that return a result: The
run()
method provided by theRunnable
interface doesn't return a result, but with executors, you can have tasks that return a result - Execute a list of tasks with a single method call
- Finish the execution of an executor and wait for its termination
- Execute tasks that return a result: The
- The ThreadPoolExecutor class: This class implements the
Executor
andExecutorService
interfaces. In addition, it includes some additional methods to get the status of the executor (number of worker-threads, number of executed tasks, and so on), methods to establish the parameters of the executor (minimum and maximum number or worker-threads, time that idle threads will wait for new tasks, and so on) and methods that allow programmers to extends and adapt its functionality. - The Executors class: This class provides utility methods to create
Executor
objects and other related classes.