CPSC 441, Fall 2014
Lab 3b: Threads for Your Server
This lab is a continuation of Lab 3. After finishing the single-threaded web server required for that lab, you should add support for threads. To do that, you will create a thread pool and you will use an ArrayBlockingQueue to feed connections from the main() routine to the thread pool.
The combined lab is due before the beginning of class next Friday. You should copy your work into your homework folder in /classes/cs441/homework. To make grading easier for me, you should set your server to use /classes/cs441/javanotes as the directory that contains its file, before you submit it.
For background material on threads, you can read Chapter 12 of my on-line programming textbook. I encourage you to read as much of that chapter as you have time for, since threads are an important topic. However, you should look at, in particular: Subsection 12.1.1, which has the most basic information about using threads in Java; Subsection 12.3.3 on the producer-consumer problem and blocking queues; Subsection 12.4.3 on adding threads to a server; and Subsection 12.4.4 on using a thread pool in a server. You might also look at Subsection 12.1.3, which talks about shared resources, race conditions, and synchronized methods; these are fundamental issues in multithreaded programming, but they are handled internally by the blocking queue that you will use in your program.
The sample program DateServerWithThreads.java, which was handed out in class, shows how to use a thread pool in a server. You can find a copy of the program in /classes/cs441.
Adding the Thread Pool
You will need a subclass of Thread to represent the threads in the thread pool. The run method in that class should run in an infinite loop in which it takes a Socket from the queue and handles the connection with that Socket. This is pretty much identical to what is done in DateServerWithThreads.java, except that you will probably want to call a subroutine to do the actual connection handling.
The main() routine needs to create the ArrayBlockingQueue that will be used to pipe connections to the thread pool. Then, main() needs to create the threads that make up the thread pool and start them running. This must be done before any connections can be accepted by the server. As the main() routine accepts connections, it simply puts the connected sockets into the queue. Ordinarily, this will take essentially no time.
That's really all there is to it. The ArrayBlockingQueue is designed to take care of "synchronization" that is needed when several threads all use a shared resource (the queue in this case), and the put() and take() methods in the ArrayBlockingQueue take care of any blocking that is needed when a thread wants to enqueue or dequeue an item.
Managing the Log
There is still, however, one synchronization issue: The "log" to which the server writes information about connections is a shared resource, since all the threads write their messages to the same destination. Messages from different threads will be mixed together in the output. If a thread writes several messages about some connection, there is no reason to expect those messages to be on consecutive lines in the log. It is even possible, depending on the characteristics of the output device, for a line from one thread to show up in the middle of a line from another thread. This problem needs a solution.
A simple solution is for the thread to write all of its messages about a given connection in a single call to a synchronized method. A nicer solution might be to have another thread, defined by another class, to do the logging, and to pipe the messages from the thread pool to the logger thread using a blocking queue. The messages could be represented as Strings or as objects belonging to some kind of LogMessage class.