Wednesday, January 5, 2011

[TECH] java synchronization with junctions

The recommended synchronization mechanism in Java is, of course, the synchronized keyword. I have nothing against it, except that involves a lot of typing: first you have to create a new class to encapsulate the state that needs to be protected, then you have to write a bunch of accessor methods. Used correctly, a few volatile variables here and there offer a lightweight solution. The only problem is you can't easily block on a condition that is based on volatile variables. To remedy the situation, I've just defined and implemented the "junction" abstraction.

Here's the idea: A junction protects one or more volatile variables (usually just one). Where the variables reside is not known to the junction object (the junction object never accesses the variables). A thread that modifies any of the protected volatile variables must follow the modifications with a call to the junction's changed() method. Whereupon all threads waiting for a condition are woken up to test the condition. An example should make things clearer:
/***
 * Example.java
 * copyright (c) 2011 by andrei borac
 ***/

import java.io.*;

import zs42.junction.*;

public class Example
{
  static final int MAX = 1024;
  
  static Junction     stringj = new BlockingJunction();
  static volatile int stringc = 0;
  static String[]     strings = new String[MAX];
  
  public static void main(String[] args)
  {
    (new Thread()
      {
        public void run()
        {
          Junction.Waiter waiter = stringj.waiter();
          
          int stringi = 0;
          
          while (true) {
            // wait for a new string
            while (waiter.waitfor(stringc > stringi));
            String current = strings[stringi++];
            
            // null string indicates done
            if (current == null) break;
            
            // print the string
            System.out.println(current);
          }
        }
      }).start();
    
    (new Thread()
      {
        void pause()
        {
          try {
            Thread.currentThread().sleep(500);
          } catch (InterruptedException e) {
            // ignored
          }
        }
        
        void append(String[] currents)
        {
          // append strings, making sure to update the volatile just once, at the end
          {
            int stringi = stringc;
            
            for (String current : currents) {
              strings[stringi++] = current;
            }
            
            stringc = stringi;
          }
          
          // notify the junction that volatiles have been changed
          stringj.changed();
        }
        
        public void run()
        {
          append(new String[] { "hello", "world" });
          pause();
          append(new String[] { "how", "are", "you" });
          pause();
          append(new String[] { "this", "is", "the", "last", "installment", null });
        }
      }).start();
  }
}

Notice the simplicity of the "blocking" loop:
while (waiter.waitfor(stringc > stringi));
While it appears to be a spinning, it does actually block in waitfor. Also the waitfor call returns true each time it needs to test the condition. That way it doesn't need to know which volatile variables are involved in the condition and there is no need to express the condition to be tested in a way in which it could be evaluated in the body of waitfor.

Besides BlockingJunction, there are also SpinningJunction (keep testing the condition in a loop) and YieldingJunction (yield the processor but test once each time the current thread is scheduled). These might be useful in cases where latency is critical.

The code for the junction primitives is available on the Downloads page of my main website (www.zerosum42.com), and may be redistributed under the terms of the "zlib license."

One last thing: It is possible one or more readers will go completely ape at the idea of synchronizing using volatile. It really does work and is really guaranteed to work according to the new Java memory model. Of course, it is somewhat easier to get things wrong with volatile, so do study the memory model in detail before attempting this.

3 comments:

  1. Thread.currentThread().sleep(500);

    should be written as

    Thread.sleep(500);

    since sleep is a static method

    ReplyDelete
  2. It seems Thread.currentThread().sleep(x) and Thread.sleep(x) would have the same effect. Of course, Thread.sleep(x) is cleaner. Well spotted.

    ReplyDelete
  3. Thank you for the blog. Found it interesting and useful. Java is a general purpose, high-level, class-based and object-oriented programming language. And we provide Java training in Chennai at Fita.

    ReplyDelete