Running on Java 22-ea+27-2262 (Preview)
Home of The JavaSpecialists' Newsletter

292StartingGun Synchronizer

Author: Dr Heinz M. KabutzDate: 2021-08-31Java Version: 7+Category: Concurrency
 

Abstract: CountDownLatch is easy to understand, but can be hard to use, especially the await() method that throws InterruptedException. In this newsletter we show how we can create our own synchronizer based on the AbstractQueuedSynchronizer.

 

Welcome to the 292nd edition of The Java(tm) Specialists' Newsletter, sent to you from, yes you guessed it, the stunning Island of Crete. Greece took the painful path of a strict lockdown from November until May, together with making it easy to get vaccinated for those who wanted to. The hope was that tourists would come flocking to Greece during the summer months, bringing much needed economic relief. It seems to have paid off. 2021 was not nearly as good as 2019, but there are a lot of tourists here, enjoying the beaches, fine food, good weather. We even have a miniature version of JCrete this year, consisting of 100% Java Champions, all vaccinated of course.

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

StartingGun Synchronizer

A few months ago, I started recording detailed walkthroughs of how the concurrency utilities work in Java. So far I have created a course about the ArrayBlockingQueue and the CopyOnWriteArrayList/Set.

The next course that I want to create is about the AbstractQueuedSynchronizer. I had known about the class for a very long time and I knew that it was at the cornerstone of many concurrency classes. However, I had never investigated how it truly worked, until now. Recently I was looking at a new open source project that provides a parser for log files and they were using the CountDownLatch in a rather awkward fashion:

private final CountDownLatch deployed = new CountDownLatch(1);

public void awaitDeployment() {
  try {
    deployed.await();
  } catch (InterruptedException e) {
    LOGGER.throwing("JVMEventSource", "awaitDeployment", e);
  }
}

public void start() {
  deployed.countDown();
}

I shudder when I see InterruptedException handled like this. Interruptions are orthogonal to the code and we never know when a thread might be interrupted, nor which other thread has issued the interrupt. We also do not know who might want to know later whether our thread was interrupted. Before throwing the InterruptedException, the interrupt status on the current thread is cleared. Thus we would lose that status on the thread if we catch the InterruptedException. On the other hand, we also do not want to propagate the exception from the method, otherwise the caller will have to deal with it. Most likely we actually want to block the caller until we are completely deployed and ready for action. When I saw that code, I immediately refactored it into an uninterruptible wait, like so:

private final CountDownLatch deployed = new CountDownLatch(1);

public void awaitDeployment() {
  boolean interrupted = Thread.interrupted();
  while(true) {
    try {
      deployed.await();
      if (interrupted) Thread.currentThread().interrupt();
      break;
    } catch (InterruptedException e) {
      interrupted = true;
    }
  }
}

public void start() {
  deployed.countDown();
}

After inspecting the rest of the project, I found 3 other places that were coded in a similar fashion. In all cases the CountDownLatch had size one and these were used as a way to indicate whether the service had been started up. A starting gun, if you will. Even though the code above will work, it does have the disadvantage that every time the thread is interrupted, a new InterruptedException would be constructed. Exceptions are particularly expensive to create because of the stack trace. It is typically best to avoid that cost.

Fortunately I had already done some research for my new mini-course on the AbstractQueuedSynchronizer and thus knew that it would be trivial to make a new synchronizer, similar to the CountDownLatch, but which would do precisely what we were looking for. Since the CountDownLatch is in the public domain, we are allowed to copy and paste the code to our heart's content. I did that to get the initial structure and then did some refactoring to simplify it a bit. One thing to note is that some of the parameters are not used in this synchronizer. I thus name them "unused" so as to avoid confusion.

import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * A StartingGun for services, where we can wait for one
 * particular service to start up.
 *
 * Instead of {@link java.util.concurrent.CountDownLatch}, we
 * have a custom concurrency utility called StartingGun, which is
 * like a CountDownLatch with a count of 1 and where the
 * awaitUninterruptibly() method does not throw an exception.
 * Similarly to the CountDownLatch, this uses the {@link
 * AbstractQueuedSynchronizer} for the synchronization.
 *
 * @author Dr Heinz M. Kabutz
 */
public class StartingGun {
  private static final int UNUSED = 0;

  /**
   * Synchronization control For StartingGun.
   * Uses AQS state to represent whether we are ready.
   */
  private final AbstractQueuedSynchronizer sync =
      new AbstractQueuedSynchronizer() {
        private static final int SUCCESS = 1;
        private static final int FAILURE = -1;
        private static final int READY = 1;

        protected int tryAcquireShared(int unused) {
          return (getState() == READY) ? SUCCESS : FAILURE;
        }

        protected boolean tryReleaseShared(int unused) {
          setState(READY);
          return true;
        }
      };

  /**
   * Wait for the starting gun to fire, without propagating the
   * InterruptedException.
   */
  public void awaitUninterruptibly() {
    sync.acquireShared(UNUSED);
  }

  /**
   * Indicate that the service is ready for operation.
   */
  public void ready() {
    sync.releaseShared(UNUSED);
  }
}

My StartingGun has a few minor differences with the CountDownLatch. First off, we call sync.acquireShared(), whereas the CountDownLatch calls sync.acquireSharedInterruptibly(). This means that interrupts are saved until we are done with waiting for the state, without causing unnecessary exceptions. Also, since this is meant to be used with a single service, we don't need a count. It is more like a single boolean latch that is either open or closed.

Here is an example of how I used it to simplify the code:

private final StartingGun deployed = new StartingGun();

public void awaitDeployment() {
    deployed.awaitUninterruptibly();
}

public void start() {
    deployed.ready();
}

Nice and easy!

Kind regards

Heinz

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '23 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...