Java Concurrent Api
Java Concurrent Api
In the previous sections you got familiar with concurrent programming by using the low-
level API such as working with threads (create, start, pause, stop), using locks and
synchronized keyword. That’s good for the basics and simple usages.
For more advanced tasks, using the low-level API is time-consuming and error-prone.
That’s why the high-level concurrency API is designed to help programmers easily
implement more complex multi-threading tasks. Programmers can focus on the
business logic of the tasks rather than getting busy in the low-level details.
In the previous lessons, you learned how to use Lock and Condition in
the java.util.concurrent.locks package. I will talk about atomic classes in the next
lesson, and today I focus on helping you get familiar with the concurrent utilities in
the java.util.concurrent package.
With the support of high-level concurrency API, you can do the following things (but not
limited to):
Executing tasks by multiple threads that are managed in a thread pool. You don’t
have to manage the thread pool yourself, just choose a kind of pool you want
and submit the tasks. This can be done via various implementations
of Executor.
Running a task that computes a value (Callable) and waiting for the result
(Future). This can be done by using an ExecutorService.
Scheduling a task to be executed after a given delay, or to be executed
periodically at a fixed rate or fixed delay. This can be done by using
the ScheduleExecutorService with ScheduleFuture.
Instead of creating new threads when new tasks arrive, a thread pool keeps a number
of idle threads that are ready for executing tasks as needed. After a thread completes
execution of a task, it does not die. Instead it remains idle in the pool waiting to be
chosen for executing new tasks.
You can limit a definite number of concurrent threads in the pool, which is useful to
prevent overload. If all threads are busily executing tasks, new tasks are placed in a
queue, waiting for a thread becomes available.
That’s basically how thread pool works. In practice, thread pool is used widely in web
servers where a thread pool is used to serve client’s requests. Thread pool is also used
in database applications where a pool of threads maintaining open connections with the
database.
Implementing a thread pool is a complex task, but you don’t have to do it yourself. As
the Java Concurrency API allows you to easily create and use thread pools without
worrying about the details.
* Understanding Executors:
t.start();
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
The Java Concurrency API defines the following 3 base interfaces for executors:
You can create an executor by using one of several factory methods provided by the
Executors utility class. Here’s to name a few:
In case the factory methods do not meet your need, you can construct an executor
directly as an instance of either ThreadPoolExecutor or ScheduledThreadPoolExecutor,
which gives you additional options such as pool size, on-demand construction, keep-
alive times, etc.
The following code snippet shows you a simple example of executing a task by a single-
threaded executor:
import java.util.concurrent.*;
System.out.println(Thread.currentThread().getNa
me());
};
pool.execute(task);
pool.shutdown();
}
}
As you can see, a Runnable task is created using anonymous-class syntax. The task
simply prints the thread name and terminates. Compile and run this program and you
will see the output something like this:
pool-1-thread-1
Note that you should call shutdown() to destroy the executor after the thread completes
execution. Otherwise, the program is still running afterward. You can observe this
behavior by commenting the call to shutdown.
this.clockName = clockName;
try {
Thread.sleep(1000);
ex.printStackTrace();
}
}
This class represents a countdown clock that counts a number from 5 down to 0, and
pause 1 second after every count. Upon running, it prints the current thread name,
follows by the clock name and the count number.
Let’s create an executor with a cached thread pool to execute 4 clocks concurrently.
Here’s the code:
import java.util.concurrent.*;
pool.execute(new CountDownClock("A"));
pool.execute(new CountDownClock("B"));
pool.execute(new CountDownClock("C"));
pool.execute(new CountDownClock("D"));
pool.shutdown();
Compile and run this program, you will see that there are 4 threads executing the 4
clocks concurrently:
Modify this program to add more tasks e.g. add more 3 clocks. Recompile and run the
program again, you will see that the number of threads is as equal as the number of
submitted tasks. That’s the key behavior of a cached thread pool: new threads are
created as needed.
Here, we create an executor with a pool of maximum 2 concurrent threads. Keep only 4
task (4 clocks) submitted to the executor. Recompile and run the program you will see
that there are only 2 threads executing the clocks:
The clocks A and B run first, while the clocks C and D are waiting in the queue. After A
and B completes execution, the 2 threads continue executing the clocks C and D. That’s
the key behavior of a fixed thread pool: limiting the number of concurrent threads and
queuing additional tasks.
Recompile and run the program, you will see that there’s only one thread executing the
4 clocks sequentially:
That’s the key behavior of a single-threaded executor: queue tasks to execute in order,
one after another.
The ExecutorService interface defines a method that allows us to execute such kind of
task:
Here, the type parameter T is the return type of the task. You submit a task that
implements the Callable interface which defines only one method as follows:
public T call();
The purpose of the Callable interface is similar to Runnable, but its method returns a
value of type T.
Once the task is submitted, the executor immediately returns an object of
type Future representing the pending results of the task, for example:
Callable< Integer > task = new task that returns an Integer value;
Then you can invoke the Future’s get() method to obtain the result upon successful
completion. There are two overloads of this method defined as follows:
T get();
The first overload version waits if necessary for the computation to complete and then
retrieves its result:
This method blocks the current thread to wait until the computation completes and
returns the value. In case you want to wait only for a specified amount of time, use the
second overload version:
This call wais if necessary for at most 2 seconds for the computation to complete, and
then retrieves the result if available. If the task takes longer time to complete, the call
returns null.
Suppose that we have two tasks: the first calculates the factorial value of N numbers,
and the second computes the sum of N numbers.
import java.util.concurrent.*;
private int n;
public FactorialCalculator(int n) {
this.n = n;
int result = 1;
result = result * i;
try {
Thread.sleep(5000);
ex.printStackTrace();
return result;
Here we use the sleep() method to fake the computation time. And code for the
second task:
import java.util.concurrent.*;
private int n;
public SumCalculator(int n) {
this.n = n;
int sum = 0;
try {
Thread.sleep(2000);
ex.printStackTrace();
return sum;
The following program submits two tasks above to a fixed thread pool executor:
import java.util.concurrent.*;
try {
ex.printStackTrace();
}
pool.shutdown();
Run this program and observe the result. The first task, SumCalculator takes 2 seconds
to complete and you see the result displayed after 2 seconds:
The second task, FactorialCalculator takes 5 seconds to complete, so you see the
result 3 seconds after the first result:
In addition, the Future interface provides methods for checking the completion status:
All these methods return a ScheduleFuture object which is a Future with an additional
method for checking the remaining delay time:
import java.util.concurrent.*;
ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
System.out.println("Hi!");
};
scheduler.schedule(task, 5, TimeUnit.SECONDS);
scheduler.shutdown();
As you can see, this program simply prints the message “Hi!” after a delay of 5 seconds,
and terminates.
Next, the following program plays a sound ‘beep’ for every 2 seconds:
import java.util.concurrent.*;
System.out.print("\007");
ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new BeepClock(), 4, 2,
TimeUnit.SECONDS);
Notice that, with the execution of periodic tasks, do not call shutdown() on the executor
because it causes the program to terminate immediately.
The following is a more complex example that uses a pool of 3 threads to schedule 3
count down clocks to execute concurrently:
import java.util.concurrent.*;
ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(3);
CountDownClock clock1 = new CountDownClock("A");
scheduler.scheduleWithFixedDelay(clock1, 3, 10,
TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(clock2, 3, 15,
TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(clock3, 3, 20,
TimeUnit.SECONDS);
Here, you can see 3 clocks A, B and C are scheduled to start at the same time, after an
initial delay of 3 seconds, but their periodic delay times are different. The following
screenshot shows output of this program:
You can use the returned ScheduleFuture object to cancel the tasks. This updated
version of the program above stops the 3 clocks after 2 minutes, by using another
schedule task:
import java.util.concurrent.*;
f1.cancel(true);
f2.cancel(true);
f3.cancel(true);
};
Recompile and run the program again and observe the result.
* Summary:
Here I summarize the key points you have learned today:
Understand 3 types of
executors: Executor, ExecutorService and ScheduledExecutorService.
Know how to create different kinds of thread pools via several factory methods of
the Executors utility class.