ThreadPoolExecutor - 1

"Ordinary programmers sooner or later come to grips with the fact that they have many small tasks that need to be performed from time to time."

"If you're writing a game, then it's the actions that individual characters perform."

"If you're writing a web server, then it's different commands coming in from users: upload a photo, transcode it into the desired format, apply the desired template, etc."

"Sooner or later, all big tasks are broken up into a set of small, manageable tasks."

"So, given this context, a subtle question arises: how are you supposed to manage them all? What if you need to perform several hundred tasks in a minute? Creating a thread for each task wouldn't make much sense. The Java machine allocates quite a lot of resources for each thread."

"In other words, creating and destroying a thread may take more time and resources than the task itself."

"Java's creators came up with an elegant solution to this problem: ThreadPoolExecutor.

"ThreadPoolExecutor is a class with two things inside:"

A) A task queue, which you can add task to as they arise in the program.

B) A thread pool, which is a group of threads that perform these tasks.

"What's more, the threads aren't destroyed once a task is finished. Instead, they fall asleep in order to be ready to start a new task as soon as it appears."

"When you create a ThreadPoolExecutor, you can set the maximum number of threads to be created and the maximum number of tasks that can be queued. In other words, you can limit the number of threads to 10, for example, and the number of queued tasks to 100."

"This is how ThreadPoolExecutor works:"

1) When you add a new task, it is placed at the end of the queue.

2) If the queue is full, an exception is thrown.

3) Upon completing a task, each thread takes the next task from the queue and starts executing it.

4) If there are no tasks in the queue, a thread goes to sleep until new tasks are added.

"The approach, where we limit the number of worker threads, is advantageous in that the more threads we have, the more they interfere with each other. It is much more effective to have 5-10 worker threads and a long queue of tasks than to create 100 threads for a surge of tasks, which will compete with each other for resources: memory, processor time, database access, etc."

"Here's an example of ThreadPoolExecutor in action:"

Example
ExecutorService service = Executors.newFixedThreadPool(2);

for(int i = 0; i < 10; i++)
{
 service.submit(new Runnable() {
    public void run()
    {
     // Here we download something big from the Internet.
    }
 });
}

"Uh, I don't see it…"

"A ThreadPoolExecutor object is created when the newFixedThreadPool method is called."

So, it's very easy to use. As soon as you add a task to it with the submit method, it:

A) Wakes a sleeping thread, if there is one, to execute the task.

B) If there's no waiting thread, then it creates a new one for the task.

C) If the maximum number of threads is reached, then it simply puts the task at the end of the queue.

"I deliberately included «here we download something big from the Internet» in the example. If we have 100 tasks «download something big from the Internet», then it makes no sense to run a lot of them at the same time—we'll be held back by the bandwidth limit of our Internet connection. In this case, a couple of threads should be enough. This is what you see in the example above:"

ExecutorService service = Executors.newFixedThreadPool(2);

"It turns out that working with a bunch of tasks isn't so difficult."

"Yes. Even easier than you might imagine. But Kim will tell you about that."