You are on page 1of 12

Exercise 1: ThreadPoolExecutor

In this exercise, you will learn how to create a pool of threads using the ThreadPoolExecutor
class. You also will learn how these pool of threads are used to execute tasks.

1. Build and run a server that receives connection request (using


ThreadPoolExecutor)
2. Build and run two instances of clients

(1.1) Build and run a server that receives connection request (using
ThreadPoolExecutor)

0. Start NetBeans IDE if you have not done so yet.


1. Create a new NetBeans project

• Select File->New Project (Ctrl+Shift+N). The New Project dialog box appears.
• Under Choose Project pane, select General under Categories and Java
Application under Projects. Click Next.
• Under Name and Location pane, for the Project Name field, type in
ServerUsingThreadPoolExecutor as project name.
• For Create Main Class field, type in ServerUsingThreadPoolExecutor. (Figure-
1.10 below)
• Click Finish.

Observe that ServerUsingThreadPoolExecutor project appears and IDE generated


ServerUsingThreadPoolExecutor.java is displayed in the source editor window of NetBeans
IDE.
2. Modify the IDE generated ServerUsingThreadPoolExecutor.java as shown in Code-1.11
below. Study the code by paying special attention to the bold fonted parts.

This class acts as a listener on an IP port and echo characters sent to it when a connection is
made. This is similar in functionality to a web server type application. Since we want to be able
to handle lots of short living connections simultaneously, we would create a separate thread of
execution for each connection. Since thread creation is a costly operation to the JVM, the best
way to achieve this is to create and use a pool of threads. A thread in this pool then can be re-
used by a new connection when a previous connection is done with it.

For this example, we use an implementation of the Executor interface, ThreadPoolExecutor. To


create a new instance of this, we must also first create a Queue to be used for the pool, which in
this example is an ArrayBlockingQueue which provides a fixed sized, queue which is protected
to ensure multiple threads can add items without contention problems.
1
import java.net.*;
import java.io.*;
import java.util.concurrent.*;

public class ServerUsingThreadPoolExecutor {

private final static int MAX_THREADS = 2;

private final ServerSocket serverSocket;


private final ThreadPoolExecutor pool;
private final ArrayBlockingQueue<Runnable> workQueue;

/**
* Constructor
**/
public ServerUsingThreadPoolExecutor(int port, int poolSize) throws IOException {
/* Create a new ServerSocket to listen for incoming connections */
serverSocket = new ServerSocket(port);

/* In order to create a pool of threads, we must first have a queue


* that will be used to hold the work tasks before they are executed
* For this example we use a ArrayBlockingQueue that can hold the
* same number of tasks as we have maximum threads
*/
workQueue = new ArrayBlockingQueue<Runnable>(MAX_THREADS);

/* Now create a ThreadPool. The initial and maximum number of


* threads are the same in this example. Please note that the
* MAX_THREADS is set to 2.
*/
pool = new ThreadPoolExecutor(MAX_THREADS, MAX_THREADS,
60, TimeUnit.SECONDS, workQueue);
}

/**
* Service requests
**/
public void serviceRequests() {
int count = 1;
int qLength = 0;

try {
for (;;) {
if ((qLength = workQueue.size()) > 0)
System.out.println("Queue length is " + qLength);

2
pool.execute(new ConnectionHandler(serverSocket.accept(), count++));
}
} catch (IOException ioe) {
System.out.println("IO Error in ConnectionHandler: " + ioe.getMessage());
pool.shutdown();
}
}

/**
* Main entry point
*
* @param args Command line arguments
**/
public static void main(String[] args) {
System.out.println("Listening for connections...");
ServerUsingThreadPoolExecutor ce = null;

try {
ce = new ServerUsingThreadPoolExecutor(8100, 4);

/*
* Serve incoming connection request until interrupted.
*/
ce.serviceRequests();
} catch (IOException ioe) {
System.out.println("IO Error creating listener: " + ioe.getMessage());
}
}
}
Code-1.11: ServerUsingThreadPoolExecutor.java

3. Write ConnectionHandler.java as shown in Code-1.12 below.

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class ConnectionHandler implements Runnable {


private final Socket socket;
private final int connectionID;

/**
* Constructor
*
* @param socket The socket on which incoming data will arrive

3
**/
public ConnectionHandler(Socket socket, int connectionID) {
this.socket = socket;
this.connectionID = connectionID;
}

/**
* run method to do the work of the handler
**/
public void run() {
System.out.println("Connection " + connectionID + ", started");

try {
InputStream is = socket.getInputStream();

// Loop to do something with the socket here


while (true) {
byte[] inData = new byte[100];

/* If the number of bytes read is less than zero then the connection
* has been terminated so we end the thread
*/
if (is.read(inData) < 0)
break;

System.out.println("[" + connectionID + "]: " + new String(inData));


}
} catch (IOException ioe) {
// Ignore
}

System.out.println("Connection " + connectionID + ", ended");


}
}
Code-1.12: ConnectionHandler.java

4. Build and run the project

• Right click ServerUsingThreadPoolExecutor project and select Run Project.


• Observe the result in the Output window. (Figure-1.13 below) The server is
waiting for connection request from clients.

Listening for connections...


Figure-1.13: Result of running ServerUsingThreadPoolExecutor application

4
Trouble-shooting: If you see the following error condition, it is highly likely due to the fact that
you have not terminate the server process that uses the same port - port 8100 in this example.

IO Error creating listener: Address already in use: JVM_Bind

Solution: Terminate the previously started process that uses the same port. Click Runtime tab
window and expand Processes node and then right click the process and select Terminate
Process. Or as a brutal method, exiting the NetBeans IDE will do it as well. Also you can use
the different port number other than 8100. In this case, you will also need to change the hard-
coded port number of the client.

Solution: This exercise up to this point is provided as a ready-to-open-and-run NetBeans project


as part of hands-on lab zip file. You can find it as
<LAB_UNZIPPED_DIRECTORY>/javase5concurrency/samples/ServerUsingThreadPool
Executor. You can just open it and run it.

return to top of the exercise

(1.2) Build and run two instances of ConnectionClient

1. Create a new NetBeans project

• Select File->New Project (Ctrl+Shift+N). The New Project dialog box appears.
• Under Choose Project pane, select General under Categories and Java
Application under Projects. Click Next.
• Under Name and Location pane, for the Project Name field, type in
ConnectionClient as project name.
• For Create Main Class field, type in ConnectionClient.
• Click Finish.

• Observe that ConnectionClient project appears and IDE generated


ConnectionClient.java is displayed in the source editor window of NetBeans IDE.

2. Modify the IDE generated ConnectionClient.java as shown in Code-1.21 below. Study the
code by paying special attention to the bold fonted parts.

import java.net.*;
import java.io.*;
import java.util.concurrent.*;

/**
* ConnectionClient pass characters from the keyboard to a socket. Simplified version of

5
* telnet
*/
public class ConnectionClient {

/**
* Main entry point
*
* @param args Command line arguments
**/
public static void main(String[] args) {
String host = null;
int port = 0;

if (args.length < 2) {
host = "127.0.0.1";
port = 8100;
}

OutputStream os = null;

try {
// The Socket() call returns only when the connection
// request from this client is accepted by the server.
Socket s = new Socket(host, port);
os = s.getOutputStream();
System.out.println("Connection established to server. Type characters and press
<ENTER> to send");
System.out.println("Type EXIT and press <RETURN> to exit");

/* Read data from the standard input and send it to the remote socket */
while (true) {
byte[] inData = new byte[100];

System.in.read(inData);
String inString = new String(inData);

if (inString.substring(0, 4).compareTo("EXIT") == 0)
System.exit(1);

os.write(inData);
}
} catch (Exception e) {
System.out.println("Failed to connect to remote host: " +
e.getMessage());
}
}

6
}
Code-1.21: ConnectionClient.java

3. Build and run the project

• Right click ConnectionClient project and select Run Project.


• Observe the result in the Output window. (Figure-1.23 below)

Connection established to server. Type characters and press <ENTER> to send


Type EXIT and press <RETURN> to exit
Figure-1.23: Result of running ConnectionClient application

• Enter some value into the Input field and press Enter key. (Figure-1.25 below) In
this example, "This is a message from ConnectionClient instance 1" is entered.

7
Figure-1.25: Enter a message

Trouble-shooting: If you see the following error condition, it is highly likely due to the fact that
you have not started the server or firewall blocks the connection request.

Failed to connect to remote host: Connection refused: connect

Solution: Start the server. Also disable firewall on your computer,


8
4. Observe the output on the server side.

• Click the ServerUsingThreadPoolExecutor tab window to see the output of the


ServerUsingThreadPoolExecutor.
• Observe that the message it received from ConnectionClient is displayed. (Figure-
1.26 below)

9
Figure-1.26: Connection request from ConnectionClient is received and message from
ConnectionClient is displayed

5. Build and run another instance of ConnectionClient.

• Right click ConnectionClient project and select Run Project. This will start the
2nd instance of the ConnectionClient.
• Observe the result in the Output window. (Figure-1.23 below)

Connection established to server. Type characters and press <ENTER> to send


Type EXIT and press <RETURN> to exit
Figure-1.23: Result of running ConnectionClient application

• Enter some value into the Input field and press Enter key. (Figure-1.27 below) In
this example, "Message from the 2nd instance of ConnectionClient by Sang Shin"
is entered.

10
Figure-1.27: Enter data from the 2nd instance of ConnectionClient

6. Observe the output on the server side.

• Click the ServerUsingThreadPoolExecutor tab window to see the output of the


ServerUsingThreadPoolExecutor.
• Observe that the message it received fromthe 2nd instance ConnectionClient is
displayed. (Figure-1.28 below)

11
Figure-1.28: Connection request from ConnectionClient is received and message from
ConnectionClient is displayed

7. Close NetBeans in order to terminate the ServerUsingThreadPoolExecutor and


ConnectionClient processes for subsequent exercises. If you skip this, you will get IO Error
creating listener: Address already in use: JVM_Bind later on when you run a server program
that shares the same port number.

12

You might also like