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

Page 4 of 6

The problem of dispatching is essentially the same problem that I discussed in the context of Observer notifications (in the March 1999 column). I don't want to synchronize dispatch(...) (Listing 1, line 56) because I don't want to disallow the addition of new operations while dispatching is in progress. Here, I've taken the easy way out and copied the list inside the synchronized statement (Listing 1, line 67). A multicaster-based solution, as discussed in the March column, could also work.

The metered_dispatch(...) (Listing 1, line 79) variant on dispatch just uses an Alarm (discussed in February 1999) to dispatch events at a fixed interval.

Listing 1: /src/com/holub/asynch/Synchronous_dispatcher.java


001  
002  
003  
004  
005  
package com.holub.asynch;
.
import java.util.*;
import com.holub.asynch.Alarm;
/***********************************************************************
  |      A synchronous notification dispatcher executes a sequence of operations sequentially. This allows two sets of linked operations to be interspersed and effectively executed in parallel, but without using multiple threads. This class is built on the JDK 1.2x LinkedList class, which must be present in the system.

(c) 1999, Allen I. Holub.
This code may not be distributed by yourself except in binary form, incorporated into a Java .class file. You may use this code freely for personal purposes, but you may not incorporate it into any commercial product without my express permission in writing.

@author Allen I. Holub
 */


006  
007  
008  
009  
010  




    /**
      |      Add a new handler to the end of the current list of subscribers.
     */


011  
012  
013  
014  
015  




    /**
      |      Add several listeners to the dispatcher, distributing them as evenly as possible with respect to the current list.
     */


016  
017  
018  
019  
020  
021  
022  
023  
024  
025  
026  
027  
028  
029  
030  
031  
032  
033  
034  
035  
036  
037  
038  
039  
040  
041  
042  
043  
044  
045  
046  
047  
048  
049  


public synchronized void add_handler( Runnable[] handlers )
    {
        if( events.size() == 0 )
        {   for( int i=0; i < handlers.length; )
                events.add( handlers[i++] );
        }
        else
        {   Object[] larger  = events.toArray();
            Object[] smaller = handlers;
            if( larger.length < smaller.length )
            {   Object[] tmp = larger;
                larger  = smaller;
                smaller = tmp;
            }
            int distribution = larger.length / smaller.length;
            LinkedList new_list = new LinkedList();
            int large_source = 0;
            int small_source = 0;
            while( small_source < smaller.length ) 
            {   for( int skip = 0; skip < distribution; ++skip )
                    new_list.add( larger[large_source++] );
                new_list.add( smaller[small_source++] );
            }
            events = new_list;
        }
    }


    /*******************************************************************
      |      Remove all handlers from the current dispatcher.
     */


050  
051  
052  
053  
054  




    /**
      |      Dispatch the actions "iterations" times. Use -1 for "forever." This function is not synchronized so that the list of events can be modified while the dispatcher is running. The method makes a clone of the event list and then executes from the clone on each iteration through the list of subscribers. Events added to the list will be executed starting with the next iteration.
     */


055  
056  
057  
058  
059  
060  
061  
062  
063  
064  
065  
066  
067  
068  
069  
070  
071  
072  
073  
074  
075  
076  
077  
public void dispatch( int iterations )
    {
        // Dispatch operations. A simple copy-and-dispatch-from-copy
        // strategy is used, here. Eventually, I'll replace this code
        // with a <code>Multicaster</code>.
        if( events.size() > 0 )
            while( iterations==-1 || --iterations >= 0 )
            {
                Object[] snapshot;
synchronized( this )
                {   snapshot = events.toArray();
                }
                for( int i = 0; i < snapshot.length; ++i )
                {   ((Runnable)snapshot[i]).run();
                    Thread.currentThread().yield();
                }
            }
    }
    /**
      |      Dispatch actions "iterations" number of times, with an action dispatched every "interval" milliseconds. Note that the last action executed takes up the entire time slot, even if the run() function itself doesn't take "interval" milliseconds to execute. Also note that the timing will be irregular if any run() method executes in more than "interval" milliseconds. If you want a time interval between iterations, but not between the operations performed in a single iteration, just insert a Runnable action that sleeps for a fixed number of milliseconds.
@param iterations`number of times to loop through the actions executing them. Use -1 to mean "forever." #param interval An action is executed every "interval" milliseconds.
     */


078  
079  
080  
081  
082  
083  
084  
085  
086  
087  
088  
089  
090  
091  
092  
093  
094  
095  
096  
097  
098  
099  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
public void metered_dispatch( int iterations, int interval )
    {
        Alarm timer = new Alarm( interval, Alarm.MULTI_SHOT );
        timer.start();
        while( iterations==-1 || --iterations >= 0 )
        {
            Object[] snapshot;
            synchronized( this )
            {   snapshot = events.toArray();
            }
            for( int i = 0; i < snapshot.length; ++i )
            {   ((Runnable)snapshot[i]).run();
                timer.await();
                timer.start();
            }
        }
        timer.stop();
    }
static public class Test
    {
        // Execute the test with:
        //  java "com.holub.asynch.Synchronous_dispatcher\$Test"
        //
public static void main( String[] args )
        {
            Synchronous_dispatcher dispatcher = 
                                        new Synchronous_dispatcher();
            dispatcher.add_handler( 
                new Runnable()
 {   public void run()
                    {   System.out.print("hello");
                    }
                }
            );
            dispatcher.add_handler(
                new Runnable()
 {   public void run()
                    {   System.out.println(" world");
                    }
                }
            );
            dispatcher.dispatch( 1 );
            dispatcher.metered_dispatch( 2, 1000 );
            //------------------------------------------------
            // Test two tasks, passed to the dispatcher as arrays
            // of chunks. Should print:
            //          Hello (Bonjour) world (monde)
            Runnable[] first_task =
            {   new Runnable(){ public void run(){ System.out.print("Hello"); }},
                new Runnable(){ public void run(){ System.out.print(" World");}}
            };
            Runnable[] second_task =
            {   new Runnable(){ public void run(){ System.out.print(" Bonjour");}},
                new Runnable(){ public void run(){ System.out.print(" Monde"  );}}
            };
            dispatcher = new Synchronous_dispatcher();
            dispatcher.add_handler( first_task  );
            dispatcher.add_handler( second_task );
            dispatcher.dispatch( 1 );
        }
    }
}


Active objects

The second architecture I'd like to discuss this month is the active object architecure. Though we can thank Greg Lavender and Doug Schmidt for the name (see Resources), the architecture has been around for a while -- the first time I saw it (dinosaur that I am) was in Intel's RMX operating system, circa 1979.

  • Print
  • Feedback

Resources