Creating and Running Threads


The Server class needs to accept incoming searches and simultaneously process any requests that have not been executed. The code to do the actual searches will be executed in a separate thread. The main thread of Server, in which the remainder of the code executes, must spawn the second thread.

Java supplies two ways for you to initiate a separate thread. The first requires you to extend the class java.lang.Thread and provide an implementation for the run method. Subsequently, you can call the start method to kick things off. At that point, code in the run method begins execution in a separate thread.

A second technique for spawning threads is to create an instance of a class that implements the interface java.lang.Runnable:

 public interface Runnable {    void run(); } 

You then construct a Thread object, passing the Runnable object as a parameter. Sending the message start to the Thread object initiates code in the run method. This second technique is often done using anonymous inner class implementations of Runnable.

It is important that you distinguish between a thread, or thread of execution, and a Thread object. A thread is a control flow that the thread scheduler manages. A Thread is an object that manages information about a thread of execution. The existence of a Thread object does not imply the existence of a thread: A thread doesn't exist until a Thread object starts one, and a Thread object can exist long after a thread has terminated.

For now, you'll use the first technique to create a thread and have the Server class extend from Thread. Thread itself implements the Runnable interface. If you extend Thread, you will need to override the run method in order for anything useful to happen.

 package sis.search; import java.util.*; public class Server extends Thread {    private List<Search> queue = new LinkedList<Search>();  // flaw!    private ResultsListener listener;    public Server(ResultsListener listener) {       this.listener = listener;       start();    }    public void run() {       while (true) {          if (!queue.isEmpty())             execute(queue.remove(0));          Thread.yield();       }    }    public void add(Search search) {       queue.add(search);    }    private void execute(Search search) {       search.execute();       listener.executed(search);    } } 

The Server class defines two fields: a ResultsListener reference and a LinkedList of Search objects, named queue. A flaw exists in the declaration of the queue referenceit is not "thread safe"! For now, your test will likely execute successfully, but the flaw could cause your application to fail. In the section Synchronized Collections in this chapter, you will learn about this thread safety issue and how to correct it.

The java.util.LinkedList class implements the List interface. Instead of storing elements in a contiguous block of memory, as in an ArrayList, a linked list allocates a new block of memory for each element you add to it. The individual blocks of memory thus will be scattered in memory space; each block contains a link to the next block in the list. See Figure 13.2 for a conceptual memory picture of how a linked list works. For collections that require frequent removals or insertions at points other than the end of the list, a LinkedList will provide better performance characteristics than an ArrayList.

Figure 13.2. A Conceptual View of a Linked List


The queue reference stores the incoming search requests. The linked list in this case acts as a queue data structure. A queue is also known as a first-in, first-out (FIFO) list: When you ask a queue to remove elements, it removes the oldest item in the list first. The add method in Server takes a Search parameter and adds it to the end of the queue. So in order for the LinkedList to act as a queue, you must remove Search objects from the beginning of the list.

The constructor of Server kicks off the second thread (often called a worker or background thread) by calling start. The run method is invoked at this point. The run method in Server is an infinite loopit will keep executing until some other code explicitly terminates the thread (see Stopping Threads in this chapter) or until the application in which it is running terminates (see Shutting Down in this chapter). When you run ServerTest in JUnit, the background thread will keep executing even after all the JUnit tests have completed. It will stop only when you close the JUnit window.

The body of the loop first checks whether the queue is empty. If not, the code removes and executes the first element in the queue. Regardless, the code then calls the Thread method yield. The yield method potentially allows other threads the opportunity to get some time from the processor before the background thread picks up again. The next section on cooperative and preemptive multitasking discusses why the yield might be necessary.

The execute method delegates the actual search to the Search object itself. When the search completes, the ResultsListener reference is sent the message executed with the Search object as a parameter.

You can represent the flow of messages in a successful search using a UML sequence diagram (Figure 13.3). The sequence diagram is a dynamic view of your system in action. It shows the ordered message flow between objects. I find that sequence diagrams can be a very effective means of communicating how a system is wired together with respect to various uses.[5]

[5] Sequence diagrams sometimes are not the best way of representing complex parallel activity. A UML model that is perhaps better suited for representing multithreaded behavior is the activity diagram.

Figure 13.3. Sequence Diagram for the Active Object Search


In a sequence diagram, you show object boxes instead of class boxes. The dashed line that emanates vertically downward from each box represents the object's lifeline. An "X" at the bottom of the object lifeline indicates its termination. The Search object in Figure 13.3 disappears when it is notified that a search has been completed; thus, its lifeline is capped off with an "X."

You represent a message send using a directed line from the sending object's lifeline to the receiver. Messages flow in order from top to bottom. In Figure 13.3, a Client object sends the first message, creates. This special message shows that the Client object is responsible for creation of a Search object. Note that the Search object starts farther down; it is not in existence until this point.

After the first message send, the Client sends add(Search) to the Server object. This message send is conceptually asynchronousthe Client need not wait for any return information from the Server object.[6] You use a half-arrowhead to represent asynchronous message sends. When it receives this add message, the Server's add method passes the Search object off to the queue. You represent the execution lifetime of the add method at the Server object using an activationa thin rectangle laid over the object lifeline.

[6] The way we've implemented it, the message send is synchronous, but since the operation is immediate and returns no information you can represent it as asynchronous.

Meanwhile, the Server thread has sent the message start to its superclass (or, more correctly, to itself). The run method overridden in Server begins execution. Its lifetime is indicated by an activation on the Server object's lifeline. Code in the run method sends the message remove(0) to the Queue to obtain and remove its first Search element. Subsequently the run method sends the message execute to the Search object and notifies the ResultsListener (via executed) when execute completes.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net