Thread Pooled Server
Jakob Jenkov
Last update: 2014-12-19
This text describes a simple thread pooled
server implemented in Java. The code is based on the multithreaded
server desbribed in the text onMultithreaded Servers.
The main difference is the server loop. Rather than starting a new
thread per incoming connection, the connection is wrapped in
aRunnableand
handed off to a thread poool with a fixed number of threads.
TheRunnable's are
kept in a queue in the thread pool. When a thread in the thread
pool is idle it will take aRunnablefrom
the queue and execute it.
Note: Thread pools are discussed in more detail
in the textThread Pools.
Here
is how the server loop looks in the thread pooled edition (the full
code is shown at the bottom of this text):
while(! isStopped()){
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if(isStopped()) {
System.out.println("Server Stopped.") ;
break;
}
throw new RuntimeException(
"Error accepting client connection", e);
}
this.threadPool.execute(
new WorkerRunnable(clientSocket, "Thread Pooled Server"));
}
The
only change in the loop from the multithreaded server to here is
the code in bold:
this.threadPool.execute(
new
WorkerRunnable(clientSocket, "Thread Pooled Server"));
Rather than starting a new thread per incoming
connection, theWorkerRunnableis
passed to the thread pool for execution when a thread in the pool
becomes idle.
Here is the code for theWorkerRunnableclass,
which is passed to the worker thread constructor:
package
servers;
import
java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.Socket;
public class
WorkerRunnable implements Runnable{
protected Socket
clientSocket = null;
protected
String serverText =
null;
public
WorkerRunnable(Socket clientSocket, String serverText) {
this.clientSocket = clientSocket;
this.serverText =
serverText;
}
public void run() {
try {
InputStream input =
clientSocket.getInputStream();
OutputStream output = clientSocket.getOutputStream();
long time = System.currentTimeMillis();
output.write(("HTTP/1.1 200 OK\n\nWorkerRunnable: " +
this.serverText + " - " +
time +
"").getBytes());
output.close();
input.close();
System.out.println("Request processed: " + time);
} catch (IOException e) {
//report exception somewhere.
e.printStackTrace();
}
}
}
Thread Pooled Server
Advantages
The
advantages of a thread pooled server compared to a multithreaded
server is that you can control the maximum number of threads
running at the same time. This has certain advantages.
First
of all if the requests require a lot of CPU time, RAM or network
bandwidth, this may slow down the server if many requests are
processed at the same time. For instance, if memory consumption
causes the server to swap memory in and out of disk, this will
result in a serious performance penalty. By controlling the maximum
number of threads you can minimize the risk of resource depletion,
both due to limiting the memory taken by the processing of the
requests, but also due to the limitation and reuse of the threads.
Each thread take up a certain amount of memory too, just to
represent the thread itself.
Additionally, executing many requests concurrently will slow down
all requests processed. For instance, if you process 1.000 requests
concurrently and each request takes 1 second, then all requests
will take 1.000 seconds to complete. If you instead queue the
requests up and process them say 10 at a time, the first 10
requests will complete after 10 seconds, the next 10 will complete
after 20 seconds etc. Only the last 10 requests will complete after
1.000 seconds. This gives a better service to the clients.
Thread Pooled Server Code
Here is the full code for theThreadPooledServer:
package
servers;
import
java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class
ThreadPooledServer implements Runnable{
protected
int serverPort = 8080;
protected
ServerSocket serverSocket = null;
protected
boolean isStopped =
false;
protected
Thread runningThread= null;
protected
ExecutorService threadPool =
Executors.newFixedThreadPool(10);
public
ThreadPooledServer(int port){
this.serverPort = port;
}
public void run(){
synchronized(this){
this.runningThread = Thread.currentThread();
}
openServerSocket();
while(! isStopped()){
Socket clientSocket = null;
try {
clientSocket = this.serverSocket.accept();
} catch (IOException e) {
if(isStopped()) {
System.out.println("Server Stopped.") ;
break;
}
throw new RuntimeException(
"Error accepting client connection", e);
}
this.threadPool.execute(
new WorkerRunnable(clientSocket,