For example, the pattern of a simple server that used one threaded job to handle each
incoming connection would look as follows. Note that there are some details here that
for simplicity we have omitted— such as how the thread pool is shut down or what
to do if it is "overloaded"—
but we'll come to these issues in a moment:
import java.util.concurrent.*;
...
ExecutorService exec = Executors.newFixedThreadPool(4);
private void runServer() {
ServerSocket sock = new ServerSocket(portNo);
while (!stopRequested) {
Socket s = sock.accept();
exec.execute(new ConnectionRunnable(s));
}
}
private static class ConnectionRunnable implements Runnable {
private final Socket s;
ConnectionRunnable(Socket s) {
this.s = s;
}
public void run() {
// handle connection
}
}
Constructing a ThreadPoolExecutor: the Executors helper class
In the first line, exec will in practice be a ThreadPoolExecutor or
some subclass thereof. We could
have called the ThreadPoolExecutor constructor directly, but it has a number of parameters which
can be a bit unwieldy in the simplest case. The Executors class is a placeholder for a
number of static utility methods to facilitate construction and use of thread pools (and in principle, other
types of executors— see below).
Utility methods such as newFixedThreadPool() in fact declare that they return an implementation
of ExecutorService: an interface that ThreadPoolExecutor, and potentially other
classes in the future, implements. So in practice, that is how we will refer to our thread pool.
As you see, here we construct a thread pool that will
always have exactly four threads, but we'll see that ThreadPoolExecutor and the
Executors utility class are actually more versatile.
Our simple server then sits in a loop in some "main" thread (which calls runServer(),
continually waiting for connections.
Each time a connection comes in, it is passed to the thread pool to be executed in another thread when
one is free to take on the job. Meanwhile, the main thread can immediately get back to accepting further
incoming connections. At some point in the near future, and in one of the threads managed by
the ThreadPoolExecutor, the run() method of the passed-in ConnectionRunnable
will be executed.
Next: ThreadPoolExecutor options
This example shows a very simple case of using a ThreadPoolExecutor with default
options. In some cases, we will need to set a few more options to make the thread pool behave
in the desired way:
Note that although we declared s final in this example (just because we "may as well"
if it won't be modified), that isn't necessary for variable visibility: the synchronization inside
ThreadPoolExecutor will ensure that the fields of the constructed object are correctly
seen by the executing thread (in the terms of Goetz et al., that the runnable is "correctly published" in other words).
Did this article solve your problem? If not, you can now
post a comment or question
Java threading articles
Java threading and concurrency
Java profiling
Java performance graph index
Unless otherwise stated, the Java programming articles and tutorials on this site are written by Neil Coffey.
Suggestions are always welcome if you wish to suggest topics for Java tutorials or programming articles,
or if you simply have a programming question that you would like to see answered on this site. Most topics
will be considered. But in particular, the site aims to provide tutorials and information on topics that
aren't well covered elsewhere, or on Java performance information that is poorly described or understood.
Suggestions may be made via the Javamex blog (see the site's front page for details).
Copyright © Javamex UK 2011. All rights reserved.