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 9

More threads in an object-oriented world: Synchronous dispatchers, active objects, detangling console I/O

  • Print
  • Feedback
This month I'm picking up the architectural theme from the May 1999 Java Toolbox column, with a look at two additional architectural solutions to thread-synchronization problems. You'll remember from last month that object-oriented systems are designed in terms of synchronous and asynchronous messages, not in terms of threads of execution. (If you don't remember that, you should read the previous article.) The two strategies I presented for implementing asynchronous messages (the one-thread-per-message and thread-pool techniques) work just fine in many applications, but since multiple threads are running concurrently -- perhaps accessing the same object -- you still have to worry about interthread synchronization. This month I'll look at two additional approaches that can all but eliminate the need for synchronization between messages.

Synchronous dispatching

A synchronous dispatcher, or round-robin scheduler, solves the synchronization problem by simulating multithreading within a single Java thread. Let's start out by considering two tasks that need to be executed in parallel:

Figure 1. Two tasks to be executed in parallel

Each of these tasks naturally divides into four chunks, and each could be executed on its own thread. Let's also imagine that the four chunks have to be atomic (they cannot tolerate interruption while they're executing), but that it's not a problem if the task is preempted between chunks. The only way to get this atomicity in a normal preemptive multitasking environment is to synchronize the operations, with all the concomitant overhead and complexity.

To move from a single task divided into chunks to a synchronous dispatcher, imagine that we can break up the single task into four independent tasks, as illustrated in Figure 2.

Figure 2. A single task broken out into four independent tasks

It isn't too hard to imagine how we could implement the chunks; you could define each chunk as the run() method of a Runnable object, for example, put the objects into an array, and then write a simple scheduler to execute the objects one at a time with a sleep() or yield() between chunks:

    Runnable[] first_task = new Runnable[]
    {
        new Runnable(){ public void run(){  /* do chunk 1 here */ } },
        new Runnable(){ public void run(){  /* do chunk 2 here */ } },
        new Runnable(){ public void run(){  /* do chunk 3 here */ } },
        new Runnable(){ public void run(){  /* do chunk 4 here */ } },
    };
    for( int i = 0; i < first_task.length; ++i )
    {   first_task[i].run();
        Thread.getCurrentThread().yield();
    }


Doug Schmidt coined the term Reactor for this design pattern (see Resources). The Reactor pattern emerged from Schmidt's work on the ACE framework (see Resources), as a way to accumulate various operations that should occur when a given event is triggered. The event handler then executes the for loop. The effect is essentially the same as several threads waiting on a single condition variable that's set true by the event. However, unlike a condition variable, you have control over both the sequence of execution and the moment at which you give up that control.

  • Print
  • Feedback

Resources