토요일, 4월 20
Shadow

#029 J2SE 5.0 Concurrency

Lab Exercises

 

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.


Figure-1.10: Create a new project

  • 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.

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);

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
**/
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.
  • 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


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/ServerUsingThreadPoolExecutor.  You can just open it and run it.

(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
*  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());
}
}
}

Code-1.21: ConnectionClient.java

3. Build and run the project

  • Right click ConnectionClient project and select Run.
  • 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 below, “This is a message from ConnectionClient instance 1” is entered.


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,


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)


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.  This will start the 2nd instance of the ConnectionClient.

  • Observe the result in the Output window has a ConnectionClient (run) #2 tab. (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 and press Enter key. (Figure-1.27 below)  In this example, “Message from the 2nd instance of ConnectionClient by Sang Shin” is entered.


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)


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.


Figure-1.29: Exit NetBeans IDE to terminate the processes

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/ConnectionClient.  You can just open it and run it.

Summary

In this exercise, you learned 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.

Exercise 2: Implement Runnable interface

In the previous exercise, you created your own instance of ThreadPoolExecutor.  There is a utility method in the Executors class that can be used to get a ThreadPoolExecutor more easily than by instantiating your own.

In this exercise, you are going to rewrite the ServerUsingThreadPoolExecutor code to use the Executors utility class.  We will rename the file to ServerUsingExecutors.

  1. Build and run a server that receives conenction requests (using Executors)
  2. Build and run clients

(2.1) Build and run a server that receives connection requests (using Executors)

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 ServerUsingExecutors as project name.
  • For Create Main Class field, type in ServerUsingExecutors.
  • Click Finish.

  • Observe that ServerUsingExecutors project appears and IDE generated ServerUsingExecutors.java is displayed in the source editor window of NetBeans IDE.

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

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

public class ServerUsingExecutors {
private final static int MAX_THREADS = 3;

private final ServerSocket serverSocket;
private final ExecutorService pool;

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

/*  Use the Exectors factory method to get a ThreadPool  */
pool = Executors.newFixedThreadPool(MAX_THREADS);

}

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

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

}

public static void main(String[] args) {
System.out.println(“Listening for connections…”);
ServerUsingExecutors ce = null;

try {
ce = new ServerUsingExecutors(8100, 4);
ce.serviceRequests();
} catch (IOException ioe) {
System.out.println(“IO Error creating listener: ” + ioe.getMessage());
}
}
}

Code-2.11: ServerUsingExecutors.java

3. Write ConnectionHandler.java as shown in Code-2.12 below.  This is the same code you’ve created in the Exercise 1 above.

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
**/
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-2.12: PrintNameRunnable.java

4. Build and run the project

  • Right click ServerUsingExecutors project and select Run.
  • 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 ServerUsingExecutors application


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.  Exit the NetBeans IDE will do it.  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/ServerUsingExecutors.  You can just open it and run it.

(2.2) Create and start a thread by implementing Runnable interface – start() method is in the constructor

1. Build and run the 1st instance of ConnectionClient.

  • Right click ConnectionClient project and select Run.  This will start the 1st instance of the ConnectionClient.
  • Observe the result in the Output window. (Figure-2.21 below)
Connection established to server. Type characters and press <ENTER> to send
Type EXIT and press <RETURN> to exit

Figure-2.21: Result of running ConnectionClient application

  • Enter some value into the Input field and press Enter key.

2. 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 the 1st instance ConnectionClient is displayed.

3. Build and run the 2nd instance of ConnectionClient.

  • Right click ConnectionClient project and select Run.  This will start the 2nd instance of the ConnectionClient.
  • Observe the result in the Output window. (Figure-2.22 below)
Connection established to server. Type characters and press <ENTER> to send
Type EXIT and press <RETURN> to exit

Figure-2.22: Result of running ConnectionClient application

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 the 2nd instance ConnectionClient is displayed.

Summary

In this exercise, you learned how to use Executors utility class to create a pool of threads.

Exercise 3: Callable and Future

In this exercise, we will look at the use of the Callable interface and a Future object to allow a child thread to return a result to a parent thread.  The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception while a Callable does.

The Executors class contains utility methods to convert from other common forms to Callable classes.
 

(3.1) Callable and Future

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 CallableAndFuture as project name.
  • For Create Main Class field, type in CallableAndFuture.
  • Click Finish.

  • Observe that CallableAndFuture project appears and IDE generated CallableAndFuture.java is displayed in the source editor window of NetBeans IDE.

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

import java.util.concurrent.*;

/**
*  Demonstration of the use of a Future to return the results from a
*  Callable.  This shows the simplicity of synchronising two concurrent
*  threads
**/
public class CallableAndFuture {
/**
*  Constructor
**/
public CallableAndFuture() {
}

/**
*  Run the test
**/
public void runTest() {
/*  Use the Executors utility method to get an ExecutorService for a
*  separate thread of execution
*/
ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
System.out.println(“Starting test in first thread”);

/*  Now attempt to run a task in the second thread  */
try {
Future<String> future = threadExecutor.submit(new CallableExample());
/*  Set this sleep to either 100 0r 1000 to see the synchronisation
*  effects
*/
Thread.sleep(1000);
System.out.println(“First thread work complete.  Asking future for result”);
String result = future.get();
System.out.println(“Result from Future is ” + result);
} catch (Exception e) {
System.out.println(“Got an exception executing the test”);
System.out.println(e.getMessage());
}

/*  Shutdown the second thread so the program terminates gracefully
*/
threadExecutor.shutdown();
}

/**
*  Main entry point
*
* @param args The command line arguments
**/
public static void main(String[] args) {
CallableAndFuture fe = new CallableAndFuture();
fe.runTest();
}
}

Code-3.11: CallableAndFuture.java

3. Write CallableExample.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

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

/**
*  A simple implementation of the Callable interface that can return a value
*  to the thread that started it
**/
public class CallableExample implements Callable<String> {
/**
*  The entry point called when this object is invoked as a new thread
*  of execution
*
* @returns A String as a simple result
**/
public String call() {
System.out.println(“Starting call() method in second thread”);

try {
Thread.sleep(500);
} catch (InterruptedException ie) {
// Ignore
}

System.out.println(“Completed call() method in second thread”);
return “Finished”;
}
}

Code-3.12: SimpleThread.java

4. Build and run the project

  • Right click CallableAndFuture project and select Run.
  • Observe the result in the Output window. (Figure-3.13 below)
Starting test in first thread
Starting call() method in second thread
Completed call() method in second thread
First thread work complete.  Asking future for result
Result from Future is Finished

Figure-3.13: Result of running CallableAndFuture application

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/CallableAndFuture.  You can just open it and run it.

Summary

In this exercise, you looked at the use of the Callable interface and a Future object so that a child thread can return a result to a parent thread.

 

Exercise 4: Semaphore

For this exercise we will look at the use of a semaphore to restrict access to a fixed size pool of resources.  Semaphores can be used to provide a fixed number of simultaneous accesses to a particular resource.

“Conceptually, a semaphore maintains a set of permits. Each acquire() blocks if necessary until a permit is available, and then takes it. Each release() adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.

Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.”

(4.1) Semaphore

In this step, you are going to build an application that displays a result that is not desirable since threads are not synchronized.

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 SemaphoreExample as project name.
  • For Create Main Class field, type in ResourseUser.
  • Click Finish.

  • Observe that SemaphoreExample project appears and IDE generated ResourseUser.java is displayed in the source editor window of NetBeans IDE.

2. Modify the IDE generated ResourseUser.java as shown in Code-4.11 below.

This class creates three threads that try to access the two reources in the pool.  By using a semaphore to restrict access to the resources, this demonstrates that one of the three threads will always block when trying to aquire a resource.  The threads hold a resource for different lengths of time to illustrate this more clearly.

/**
*
*  Concurrency utilities (JSR-166) example
**/

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

/**
*  Use resources from a pool and return them at random times
**/
public class ResourceUser implements Runnable {
private ResourcePool pool;
private int threadId;
private int runCount;
private long hold;
private long pause;

/**
*  Constructor
*
* @param pool The pool to get the resource from
* @param threadId The id number of this thread
* @param runCount The number of times to run the test loop
* @param hold How long to hold the resource before releasing it
* @param pause How long to wait before trying to re-aquire the resource
**/
public ResourceUser(ResourcePool pool, int threadId, int runCount,
long hold, long pause) {
this.pool = pool;
this.threadId = threadId;
this.runCount = runCount;
this.hold = hold;
this.pause = pause;
}

/**
*  Request a resource from the pool and
*  return them after a random time period
**/
public void run() {
for (int i = 0; i < runCount; i++) {
try {
/*  Get the resource from the pool  */
System.out.println(“[” + threadId + “] trying to get resource”);
Integer resource = pool.getResource();
System.out.println(“[” + threadId + “] aquired resource”);

Thread.sleep(hold);
System.out.println(“[” + threadId + “] releasing resource”);
pool.putResource(resource);
Thread.sleep(pause);
} catch (InterruptedException ie) {
//  Silently ignore
}
}
}

/**
*  Main entry point
*
* @param args The command line arguments
**/
public static void main(String[] args) {
/*  Create a resource pool with two resources in it  */
ResourcePool pool = new ResourcePool(2);

/*  Use the Executors utility class to get a new FixedThreadPool.
*/
ExecutorService threadExecutor = Executors.newFixedThreadPool(3);

/*  Create three new resource users and start them using the previously
*  initialised thread pool
*/
ResourceUser r1 = new ResourceUser(pool, 1, 5, 400, 500);
ResourceUser r2 = new ResourceUser(pool, 2, 5, 800, 500);
ResourceUser r3 = new ResourceUser(pool, 3, 5, 1200, 500);
threadExecutor.execute(r1);
threadExecutor.execute(r2);
threadExecutor.execute(r3);

/*  Clean up the thread pool so the program terminates cleanly  */
threadExecutor.shutdown();
}
}

Code-4.11: ResourseUser.java

3. Write ResourcePool.java as shown in Code-4.12 below.

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

/**
*  Use a Semaphore to control the usage of a pool of resources.  If a
*  consumer requests a resource when all are being used the consumer
*  will block until a resource is returned and made available.
**/
public class ResourcePool {
private int poolSize;
private Semaphore available;
private Integer[] resources;
private boolean[] used;

/**
*  Constructor
*
* @param poolSize Size of fixed pool of resources
**/
public ResourcePool(int poolSize) {
this.poolSize = poolSize;

/*  Create a pool of resources (for this example just a set of Integer
*  objects.  Create a new semaphore to control access to the resources
*/
available = new Semaphore(poolSize);

used = new boolean[poolSize];
resources = new Integer[poolSize];

for (int i = 0; i < poolSize; i++)
resources[i] = new Integer(i);
}

/**
*  Get a resource.  If all are currently being used this will block
*  until one is returned to the pool.  This is a synchronised method
*  to make the code fully thread safe.
*
* @return The resource to use
**/
public Integer getResource() {
try {
available.acquire();
} catch (InterruptedException ie) {
// Ignore
}

for (int i = 0; i < poolSize; i++) {
if (used[i] == false) {
used[i] = true;
return resources[i];
}
}

return null;
}

/**
*  Return a resource to the pool
*
* @param resource The resource being returned to the pool
**/
public void putResource(Integer resource) {
/*  Note use of auto-unboxing  */
used[resource] = false;
available.release();
}
}

Code-4.12: PrintStringsThread.java

4. Build and run the project

  • Right click SemaphoreExample project and select Run.
  • Observe the result in the Output window. (Figure-4.14 below)
[1] trying to get resource
[1] aquired resource
[2] trying to get resource
[2] aquired resource
[3] trying to get resource
[1] releasing resource
[3] aquired resource
[2] releasing resource
[1] trying to get resource
[1] aquired resource
[2] trying to get resource
[1] releasing resource
[2] aquired resource
[3] releasing resource
[1] trying to get resource
[1] aquired resource
[3] trying to get resource
[2] releasing resource
[3] aquired resource
[1] releasing resource
[2] trying to get resource
[2] aquired resource
[1] trying to get resource
[3] releasing resource
[1] aquired resource
[2] releasing resource
[1] releasing resource
[3] trying to get resource
[3] aquired resource
[2] trying to get resource
[2] aquired resource
[1] trying to get resource
[2] releasing resource
[1] aquired resource
[3] releasing resource
[1] releasing resource
[2] trying to get resource
[2] aquired resource
[3] trying to get resource
[3] aquired resource
[2] releasing resource
[3] releasing resource
[3] trying to get resource
[3] aquired resource
[3] releasing resource

Figure-4.14: Result of running SemaphoreExample application

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>/javathreads/samples/SemaphoreExample.  You can just open it and run it.

5. For your own exercise, do the following.

Change the values used when creating the ResourceUser objects.  For example make each ResourceUser hold the resource for the same duration.  Recompile and run the application and see if the application behaves differently.

    ResourceUser r1 = new ResourceUser(pool, 1, 5, 1000, 500);
ResourceUser r2 = new ResourceUser(pool, 2, 5, 1000, 500);
ResourceUser r3 = new ResourceUser(pool, 3, 5, 1000, 500);

Summary

In this exercise, you learned how to use a semaphore to restrict access to a limited number of resources.

 

Exercise 5: Blocking queue

For this exercise we will look at the use of a BlockingQueue to synchronize the recording of logging messages from a number of separate threads.

(5.1) Blocking queue example

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 BlockingQueueExample as project name.
  • For Create Main Class field, type in BlockingQueueExample.
  • Click Finish.

  • Observe that BlockingQueueExample project appears and IDE generated BlockingQueueExample.java is displayed in the source editor window of NetBeans IDE.

2. Modify the IDE generated BlockingQueueExample.java as shown in Code-5.11 below.

The BlockingQueueExample class creates an ArrayBlockingQueue, which is the simplest concrete implementation of the BlockingQueue interface.  It uses this to send messages to the Logger object.

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

/**
*  Send messages to a Logger via a BlockingQueue
**/
public class BlockingQueueExample implements Runnable {
private BlockingQueue<String> messageQueue;
private int id;
private int count;
private long pause;

/**
*  Constructor
*
* @param messageQueue The quese to send messages to
* @param id The ID number of this sender
* @param count The number of messages to send
* @param pause The pause between sending each message
**/
public BlockingQueueExample(BlockingQueue<String> messageQueue, int id,
int count, long pause) {
this.messageQueue = messageQueue;
this.id = id;
this.count = count;
this.pause = pause;
}

/**
*  Run method to send the messages
**/
public void run() {
try {
for (int i = 0; i < count; i++) {
messageQueue.put(“ID ” + id + “: log message number ” + i);
Thread.sleep(pause);
}
} catch (InterruptedException ie) {
//  Ignore
}
}

/**
*  Main entry point for running test scenario
*
* @param args The command line arguments
**/
public static void main(String[] args) {

/*  For the test we will need a BlockingQueue to be used by both threads
*  Initially use an ArrayBlockingQueue which is the simplest concrete
*  implementation of the BlockingQueue interface.  The constructor takes
*  the size of the queue as a parameter
*/
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);

/*  Use the utility method from the Executors class to get an
*  ExcutorService reference that will allow us to execute a single
*  thread
*/
ExecutorService loggerExecutor = Executors.newSingleThreadExecutor();
loggerExecutor.execute(new Logger(queue));

/*  Again use the utility Executors class to get a new ExecutorService
*  for a second new thread and pass a BlockingQueueExample instance to it to run
*/
ExecutorService senderExecutor = Executors.newSingleThreadExecutor();
senderExecutor.execute(new BlockingQueueExample(queue, 1, 10, 500));
}
}

Code-5.11: BlockingQueueExample.java

3. Write Logger.java as shown in Code-5.12 below.  Study the code by paying special attention to the bold fonted parts.

The Logger class takes a BlockingQueue as an argument to the constructor to use as a queue to hold messages that need to be logged.  The BlockingQueue will ensure thread safe operations when multiple threads are trying to add elements to the list.

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

/**
*  Runnable that consumes information placed in a blocking queue.  This
*  demonstrates the simple synchonisation that can be achieved with the
*  new BlockingQueue class.
**/
public class Logger implements Runnable {

private BlockingQueue<String> messageQueue;

/**
*  Constructor
*
* @param messageQueue The queue that will be used to pass messages
* between the two threads
**/
public Logger(BlockingQueue<String> messageQueue) {
this.messageQueue = messageQueue;
}

/**
*  Run method simply takes messages from the queue when they’re there
*  and pushes them to the stdout
**/
public void run() {
try {
while (true) {
System.out.println(“LOG MSG: ” + messageQueue.take());
}
} catch (InterruptedException ie) {
// Ignore
}
}
}

Code-5.12: Logger.java

4. Build and run the project

  • Right click BlockingQueueExample project and select Run.
  • Observe the result in the Output window. (Figure-5.15 below)
LOG MSG: ID 1: log message number 0
LOG MSG: ID 1: log message number 1
LOG MSG: ID 1: log message number 2
LOG MSG: ID 1: log message number 3
LOG MSG: ID 1: log message number 4
LOG MSG: ID 1: log message number 5
LOG MSG: ID 1: log message number 6
LOG MSG: ID 1: log message number 7
LOG MSG: ID 1: log message number 8
LOG MSG: ID 1: log message number 9

Figure-5.15: Result of running BlockingQueueExample application

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>/javathreads/samples/BlockingQueueExample.  You can just open it and run it.

(5.2) Blocking queue example using multiple senders

Sending messages through a single thread to the logger as you did in the above step is not very interesting.   Modify the main method in the BlockingQueueExample class to use multiple message senders. You will need to make the following changes:

  • Create multiple instances of the MsgSender class with different identifiers and pause times
  • Change the Executor to a ThreadPoolExecutor to support the right number of threads.
  • Have the threads use different pauses between sending messages so you can see the interleaving of the different threads of execution.

1. Create a new NetBeans project called BlockingQueueExample2 by copying BlockingQueueExample project.
2. Modify the BlockingQueueExample.java as shown in Code-5.21 below.

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

/**
*  Send messages to a Logger via a BlockingQueue
**/
public class BlockingQueueExample implements Runnable {
private BlockingQueue<String> messageQueue;
private int id;
private int count;
private long pause;

/**
*  Constructor
*
* @param messageQueue The quese to send messages to
* @param id The ID number of this sender
* @param count The number of messages to send
* @param pause The pause between sending each message
**/
public BlockingQueueExample(BlockingQueue<String> messageQueue, int id,
int count, long pause) {
this.messageQueue = messageQueue;
this.id = id;
this.count = count;
this.pause = pause;
}

/**
*  Run method to send the messages
**/
public void run() {
try {
for (int i = 0; i < count; i++) {
messageQueue.put(“ID ” + id + “: log message number ” + i);
Thread.sleep(pause);
}
} catch (InterruptedException ie) {
//  Ignore
}
}

/**
*  Main entry point for running test scenario
*
* @param args The command line arguments
**/
public static void main(String[] args) {

/*  For the test we will need a BlockingQueue to be used by both threads
*  Initially use an ArrayBlockingQueue which is the simplest concrete
*  implementation of the BlockingQueue interface.  The constructor takes
*  the size of the queue as a parameter
*/
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);

/*  Use the utility method from the Executors class to get an
*  ExcutorService reference that will allow us to execute a single
*  thread
*/
ExecutorService loggerExecutor = Executors.newSingleThreadExecutor();
loggerExecutor.execute(new Logger(queue));

/*  Now create a new fixed size thread pool to allow us to run
*  (in this case) three sender threads
*/
ExecutorService senderExecutor = Executors.newFixedThreadPool(3);
senderExecutor.execute(new BlockingQueueExample(queue, 1, 5, 400));
senderExecutor.execute(new BlockingQueueExample(queue, 2, 5, 800));
senderExecutor.execute(new BlockingQueueExample(queue, 3, 5, 1200));
}
}

Code-5.21: Modified BlockingQueueExample.java

3. Build and run the project

  • Right click BlockingQueueExample project and select Run.
  • Observe the result in the Output window. (Figure-5.23 below)
LOG MSG: ID 1: log message number 0
LOG MSG: ID 1: log message number 1
LOG MSG: ID 1: log message number 2
LOG MSG: ID 1: log message number 3
LOG MSG: ID 1: log message number 4
LOG MSG: ID 1: log message number 5
LOG MSG: ID 1: log message number 6
LOG MSG: ID 1: log message number 7
LOG MSG: ID 1: log message number 8
LOG MSG: ID 1: log message number 9

Figure-5.23: Result of running BlockingQueueExample application

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>/javathreads/samples/BlockingQueueExample2.  You can just open it and run it.

Summary

For this exercise you learned how to use BlockingQueue to synchronize the recording of logging messages from a number of separate threads.

Homework exercise (for people who are taking Sang Shin’s “Java Programming online course”)

 

1. The homework is to modify the ServerUsingThreadPoolExecutor project you created in Exercise 1 above.   (You might want to create a new project by copying the MyServerUsingThreadPoolExecutor project.)  You can name the new project in any way you want but here I am going to call to call it as MyServerUsingThreadPoolExecutor.

  • Increase the maximum thread size to 3 (instead of 2)
  • Build and run 3 client instances (instead of 2)
2. Send the following files to javaprogramminghomework@sun.com with Subject as JavaIntro-javase5concurrency.

  • Zip file of the the MyServerUsingThreadPoolExecutor NetBeans project.  (Someone else should be able to open and run it as a NetBeans project.)  You can use your favorite zip utility or you can use “jar” utility that comes with JDK as following.
    • cd <parent directory that contains MyServerUsingThreadPoolExecutor directory> (assuming you named your project as MyServerUsingThreadPoolExecutor)
    • jar cvf MyServerUsingThreadPoolExecutor.zip MyServerUsingThreadPoolExecutor (MyServerUsingThreadPoolExecutor directory should contain nbproject directory)
  • Captured output screen  – name it as JavaIntro-javase5concurrency.gif orJavaIntro-javase5concurrency.jpg (or JavaIntro-javase5concurrency.<whatver graphics format>)
    • Any screen capture that shows that your program is working is good enough.  No cosmetic polishment is required.
  • If you decide to use different IDE other than NetBeans, the zip file should contain all the files that are needed for rebuilding the project – war file with necessary source files is OK.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.