Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Programming Java threads in the real world, Part 8

Threads in an object-oriented world, thread pools, implementing socket 'accept' loops

  • Print
  • Feedback

Page 3 of 5

import com.holub.asynch.Reader_writer;
import java.io.*;
class Asynchronous_flush
{
    private OutputStream    out;
    private Reader_writer   lock = new Reader_writer();
    private byte[]          buffer;
    private int             length;
    //...      synchronized void flush( )
    {   new Thread()
        {   public void run() 
            {   try
                {   lock.request_write();
                    out.write( buffer, 0, length );
                    length = 0;
                }                catch( IOException e )
                {   // ignore it.
                }
                finally
                {   lock.write_accomplished();
                }
            }       }.start();
    }
}



I've wrapped the former contents of the flush() method inside the run() method of an anonymous inner class that extends Thread. Now flush() does nothing but fire off the thread and return. This simple strategy can work for simple situations, but unfortunately it doesn't work here. Let's analyze the problems one at a time.

The main problem is that the write operation is no longer thread-safe. Simply synchronizing the flush() method locks the object only while we're in the flush() method, which isn't for very long. The actual write() operation is performed on its own thread long after flush() has returned, and the buffer may have been modified several times in the interim (or even worse, may be modified while the write is in progress). A possible solution to the synchronization problem is to make a copy of the buffer while we're synchronized, and then work from the copy when inside the (unsynchronized) auxiliary thread. The only time synchronization is necessary is while we're actually making the copy.

Because it's so easy, it would be nice if we could implement this strategy like this:

    synchronized void flush( )
    {   
        byte[] copy = buffer.clone();
        length = 0;
        new Thread()
        {   public void run()
            {   try
                {   lock.request_write();                   out.write( copy, 0, length );
                }
                catch( IOException e )
                {   // ignore it.
                }
                finally
                {   lock.write_accomplished();
                }
            }
        }.start();
    }


But this code doesn't even compile. Remember that the inner-class object -- the anonymous Thread derivative -- exists long after the method returns. Consequently, the local variables of the method can't be used by the thread (unless they're final, which, in this case, they aren't) simply because they won't exist any more; they're destroyed when flush() returns. We can copy local variables into the thread object, however.

Listing 1 solves most of these problems by using the copy strategy I just discussed. The strange-looking thing on line 24 is an "instance initializer" for the inner class. Think of it syntactically as a static initializer that isn't static -- a sort-of metaconstructor. The code in the instance initializer is effectively copied into all constructors, including the compiler-generated "default" constructor, above any code specified in the constructor itself. That is, if you have both an instance initializer and a constructor, the code in the instance initializer executes first. (The one exception to this rule is that the instance initializer is not copied into any constructor that calls another constructor using the this(optional_args) syntax. This way the code in the instance initializer is executed only once.) The syntax is pretty ugly, but there it is.

  • Print
  • Feedback

Resources
  • Links to all previous articles in this series can be found in the "Articles" section of Allen's Web site. You can find downloadable version of all the code in the same place. http://www.holub.com
  • "Sockets programming in JavaA tutorial," Qusay H. Mahmoud (JavaWorld, December 1996) Get up to speed on Java's socket support with this tutorial. http://www.javaworld.com/javaworld/jw-12-1996/jw-12-sockets.html
  • "Write your own threaded discussion forumThe communications and server components, Part 2," Michael Shoffner (JavaWorld, March 1997). http://www.javaworld.com/javaworld/jw-03-1997/jw-03-step.html
  • Java Network Programming, Elliotte Rusty Harold (O'Reilly, 1997). http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=1565922271
  • Design Patterns Elements of Reusable Object-Oriented Software, Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Addison Wesley, 1994). The Command design pattern is presented. This book is essential reading for any object-oriented designer. http://www1.fatbrain.com/asp/bookinfo/bookinfo.asp?theisbn=0201633612