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

The fact that a simple synchronization strategy is used by publish_blocking causes an additional problem in the blocking-notification scenario: the entire Publisher is locked while the subscription_list is being accessed (for the entire period required for notifications). The entire object doesn't have to be locked, however. We're synchronizing only to make access to the synchronization_list thread-safe. What we really need are two locks, one that guards the subscription_list and another that guards any other fields that we might choose to add to the Publisher. This way you couldn't add or remove subscribers while notifications were in progress, but you could call other synchronized methods of the Publisher class without blocking.

Three approaches to introducing a second lock come to mind. First, I could introduce a second lock using the roll-your-own Mutex class discussed in installment three of the current threading series. This solution is implemented in Listing 5.

Listing 5 (Mutex_publisher.java): Introducing a second lock with a Mutex


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  
import java.util.*;
import com.holub.asynch.Mutex;
import com.holub.tools.Subscriber;
public class Mutex_publisher
{
    Collection subscription_list = new LinkedList();   Mutex      guard             = new Mutex();
   public void subscribe( Subscriber subscriber ) throws InterruptedException
    {       guard.acquire(1000);
        subscription_list.add( subscriber );       guard.release();
    }
   synchronized public void cancel_subscription( Subscriber subscriber )
                                            throws InterruptedException
    {       guard.acquire(1000);
        subscription_list.remove( subscriber );       guard.release();
    }
   public void publish( ) throws InterruptedException
    {   Object[] copy;       guard.acquire(1000);
        copy = subscription_list.toArray();       guard.release();
        for( int i = 0; i < copy.length; ++i )
            ((Subscriber) copy[i]).receive("Hello world");
    }
   synchronized public void other_method()
    {   //...
    }
}


A Mutex is really overkill in the current situation, however. A second approach encapsulates my original Publisher (from Listing 3) into a container class. I've done this in Listing 6. Now subscribe and related methods aren't synchronized at all, but chain through to their synchronized cousins in the contained Subscriber(). That is, the lock associated with the contained Publisher object controls access to the subscription_list and the lock associated with the container (Wrapped_publisher) object is used by other synchronized methods. The other_method() method (Listing 6, line 19), which is synchronized, locks the container, not the Publisher.

Listing 6 (Wrapped_publisher.java): Using a container strategy


01  
02  
03  
04  
05  
06  
07  
08  
09  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
import com.holub.tools.Subscriber;
class Wrapped_publisher
{
    Publisher subscribers = new Publisher();
   public void subscribe( Subscriber subscriber )
    {   subscribers.subscribe( subscriber );
    }
   synchronized public void cancel_subscription( Subscriber subscriber )
    {   subscribers.cancel_subscription( subscriber );
    }
   public void publish( )
    {   subscribers.publish_blocking();
    }
   synchronized public void other_method()
    {                                   // Uses lock associated with "this,"
    }                                   // not the one associated with
                                        // "subscribers."
}


A third approach is to use the synchronized version of the LinkedList class, and then synchronize on the LinkedList itself. I've done that in Listing 7. Rather than creating a simple LinkedList on line 7, I've used Collections.synchronized() to wrap my list in an adapter, all of whose methods are synchronized. Now, I don't have to synchronize subscribe() or cancel_subscription(), and I don't have to synchronize when making the copy in publish(). However, I do have to explicitly synchronize on line 28 when I iterate across the list. This last approach is, I think, the most workable in practice, since it's the simplest to implement.

  • Print
  • Feedback

Resources