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 6

The Observer pattern and mysteries of the AWTEventMulticaster

  • Print
  • Feedback

Page 4 of 6

Listing 4 (Hello_world.java): Publishing to the Subscribers


01  
02  
03  
04  
05  
06  
07  
08  
09  
10  
11  
12  
13  
14  
15  
16  
17  
import com.holub.tools.Subscriber;
public class Hello_world
{
   static private Publisher  publisher  = new Publisher();
   static private Subscriber subscriber =
            new Subscriber()
           {   public void receive(Object p)
                {   System.out.println( (String) p );
                }
            };
   static public void main( String[] args )
    {   publisher.subscribe( subscriber );
        publisher.publish();            // Publish "Hello world" events
    }
}


Returning to Listing 3, you'll note that subscribe() and cancel_subscription() are synchronized, but publish() is not. Also note that publish() makes a copy of the subscription_list (on line 20) and notifies the subscribers by traversing the copy. This strategy is the one suggested in the JavaBeans specification for handling notifications in a multithreaded environment, the point being that you don't want to lock up synchronized methods of a publisher class while notifications are in progress. In any event, the new Java 2 Collection classes are (deliberately) not synchronized, so, subscribe() and cancel_subscription() must be synchronized in case one thread is trying to add a subscriber while another is removing one, or if two threads are adding at the same time, and so on. (Yes, I know about the wrappers. I'll talk about them in a moment.)

Next, there is no way to tell how long it will take for a subscriber's receive() method to execute, since it's supplied by whoever implements the subscriber. Consequently, publish() executes for an indeterminate amount of time. If publish() were synchronized, then the entire object would be locked until the notifications completed. Any thread that attempted to add or remove a subscriber, or call any other synchronized method for that matter, would block. So, publish() is not synchronized.

This lack of synchronization means that publish can't use the subscription_list directly, however. Otherwise another thread could come along and add and remove elements while notifications were in progress, perhaps corrupting the Collection used for the subscription_list. The problem is solved simplistically by synchronizing long enough to make a clean copy, then working from the copy.

Even though this notify-from-the-copy strategy is recommended by Sun, it's not ideal. First, what if a subscriber is removed from the list after the copy is made but before the notifications begin? The subscriber will be notified, even though it thinks it has cancelled the subscription. This problem exists in all AWT/Swing listeners, and there is no easy solution -- never assume that you will not be notified simply because you've removed yourself as a listener. To eliminate this problem, I've included a second, synchronized, notification method in the Publisher: publish_blocking() (Listing 3, line 27). Since this version is synchronized, nobody can add or remove listeners while notifications are in process. I don't know if this behavior is better than the nonblocking solution, but it's certainly different and more appropriate in some scenarios. Note that the publish_blocking() doesn't have to copy the subscription_list since it synchronizes access to it -- it can just use an iterator to traverse the list.

  • Print
  • Feedback

Resources