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 4 of 5

An alternative to the solution in Listing 1 would be to encapsulate the code from the instance initializer in a nonstatic method, and then call it when initializing the field:

new Thread()
{   int     length; 
    byte[]  copy = init();
    private void init()
    {   length        = Flush_example.this.length;
        byte[] copy   = new byte[length];
        System.arraycopy(Flush_example.this.buffer, 0, copy, 0, length);
        Flush_example.this.length = 0;
        return copy;
    }
    //...
}


This isn't much of an improvement over the instance initializer in clarity, and initializing length as a side effect of the init() call is particularly hideous. I've used arraycopy() rather than clone because I didn't want to mess with the CloneNotSupportedException. Exceptions are not allowed to propagate out of instance initializers.

Whatever method we use for initialization, the inner-class's construction happens in the new() on line 20 of Listing 1, while the outer-class object is locked, so the copy operation is thread-safe. The newly-created thread then acquires the writer lock and writes to the file in its own good time, using the copy for this purpose.

Listing 1: Flush_example.java


01  
02  
03  
04  
05  
06  
07  
08  
09  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  


import com.holub.asynch.Reader_writer;
import java.io.*;
class Flush_example
{
    public interface Flush_error_handler
    {   void error( IOException e );
    }
    private final OutputStream  out;
    private Reader_writer       lock = new Reader_writer();
    private byte[]              buffer;
    private int                 length;
    public Flush_example( OutputStream out )
    {   this.out = out;
    }
    //...
    synchronized void flush( final Flush_error_handler handler )
    {   new Thread()
        {   int     length; 
            byte[]  copy;
            {   length = Flush_example.this.length;
                copy   = new byte[length];
                System.arraycopy(Flush_example.this.buffer, 0, copy, 0, length);
                Flush_example.this.length = 0;
            }
            public void run()
            {   try
                {   lock.request_write();
                    out.write( copy, 0, length );
                }
                catch( IOException e )
                {   handler.error(e);
                }
                finally
                {   lock.write_accomplished();
                }
            }
        }.start();
    }
}


An exceptional problem
The next perplexing issue is what to do with the IOException. Back in the original version of the code, the exception propagated out of the flush() method to whomever called flush(). We can't do that here, because there's nobody to propagate it to -- if you start backtracking down the call stack, you'll end up back in run(), but you didn't call run(); the system did when it fired up the thread. Simply ignoring the write error, as I've been doing, isn't a good strategy for obvious reasons.

One solution to this problem is the Observer pattern discussed in March. (AWT's listeners, for example, are observers.) The method that finally catches the IOException must be a method that calls flush(), either directly or indirectly, so this method could simply register a listener and be notified when something goes wrong. In the current situation, a full-blown observer is something of an overkill, however. After all, there will only be one observer for a given I/O operation.

  • 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