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

076Asserting Locks

Author: Dr. Heinz M. KabutzDate: 2003-08-05Java Version: 1.4Category: Language
 

Abstract: A common pattern is the Java Monitor Pattern. All public methods are synchronized. Private methods would assume that the lock is already held. How can we verify this? In this newsletter we show how we can assert that we hold the lock inside the private methods.

 

Welcome to the 76th edition of The Java(tm) Specialists' Newsletter. This week's will be a lot shorter than our mega-newsletter of last week, to give you a break from having to read too much :-)

From the 20th to the 28th of September 2003, I will hopefully be visiting the Tsinghua University in Beijing, China. One of our newsletter readers was invited to present a course on Object Orientation and Java at Tsinghua University to about 50 lecturers and students from all over China. Unfortunately, due to circumstances, he was not able to go to China, and proposed me as a remote proxy instead. Therefore, if you live in Beijing, it would be great to meet with you. Please reply to this email so we can set up a time that will suit everyone. Wo Zhongwen shuo de bu hao. Huitou jian.

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

Asserting Locks

If my memory serves me correctly, JDK 1.4 was released about 18 months ago. One of the new fangled constructs that JDK 1.4 introduced was the assert keyword. In my journeys to companies, I am yet to find one that is actually using Java's assert keyword to its full potential.

Here are some reasons why I think this is:

  • Class Version Changes: To accommodate the assert keyword the class format had to be slightly changed. This means that code containing the assert keyword will only work on JDK 1.4 VMs. Even though this is only a small change, it does mean that old tools may not work with the new classes.
  • Stability of JDK 1.4: Perhaps companies are reluctant to move over to JDK 1.4.x completely. I know of companies that are still using JDK 1.1 quite happily. They usually adopt the position of "Let's wait and see...", and hope that the next version will have less bugs.
  • Ignorance: Programmers take a while to change their old habits. I still see code written in 2003 using java.util.Enumeration and java.util.Vector, usually by experienced Java programmers.

There are obviously more reasons, I would be interested to hear yours.

The reason I hesitate to use assertions is because I have a slight reservation about what will happen to the program when an AssertionError suddenly happens. I have seen code that swallowed Exception, and I would not like developers to start catching Throwable to cater for assertion errors in libraries they use.

However, I do like the ability to switch off assertion checking, even though Pragmatic Programmer [ISBN 020161622X] advises to keep them switched on.

Let us look at an example of how we could use assertions by starting with a MIDI player example. I received the Sun Core Java Technologies newsletter today, where they showed how to use MIDI from within Java. This is a follow-on from their examples, but heavily refactored.

import javax.sound.midi.*;

/**
 * This class represents an Old Song that we might want to record
 * for our listening pleasure.
 */
public abstract class OldSong {
  private final Sequencer sequencer;
  private final Track track;
  private final int resolution;
  private int pos;

  /**
   * @param key is the note that this starts with.  60 is middle C.
   * @param tempo is measured in beats per second
   */
  public OldSong(int key, int tempo, int resolution)
          throws MidiUnavailableException, 
                 InvalidMidiDataException {
    this.resolution = resolution;
    Sequence sequence = new Sequence(Sequence.PPQ, resolution);
    track = sequence.createTrack();
    makeSong(key);
    sequencer = MidiSystem.getSequencer();
    sequencer.open();
    sequencer.setSequence(sequence);
    sequencer.setTempoInBPM(tempo);
  }

  public void start() {
    sequencer.start();
  }

  protected abstract void makeSong(int key)
                            throws InvalidMidiDataException;

  protected void add(int note) throws InvalidMidiDataException {
    add(note, 1);
  }

  protected synchronized void add(int note, int length)
                           throws InvalidMidiDataException {
    addStartEvent(note);
    pos += length;
    addStopEvent(note);
  }

  protected synchronized void addSilence(int length) {
    pos += length;
  }

  /**
   * A piano teacher once told me that the first note in a bar 
   * should be emphasized.
   *
   * We assume that resolution has already been set and that we 
   * have the "this" monitor.
   */
  private int volume() {
    return ((pos % resolution) == 0) ? 100 : 70;
  }

  /**
   * We assume that we are holding the "this" monitor
   */
  private void addStartEvent(int note)
                      throws InvalidMidiDataException {
    ShortMessage message = new ShortMessage();
    message.setMessage(ShortMessage.NOTE_ON, 0, note, volume());
    track.add(new MidiEvent(message, pos));
  }

  /**
   * We assume that we are holding the "this" monitor
   */
  private void addStopEvent(int note)
                     throws InvalidMidiDataException {
    ShortMessage message = new ShortMessage();
    message.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
    track.add(new MidiEvent(message, pos));
  }
}

You can use that by overriding the class and implementing the makeSong() method to produce your own favourite song. A little bit of musical know-how does help here. In this case, I have put together an approximation of the song "As Time Goes By" from the movie Casablanca. My adaptation goes like this: "I'm not interested in politics - the problems of this world are not in my department. I'm a programmer." If you don't catch that, I suggest you watch the movie :-)

import javax.sound.midi.*;

/**
 * This program plays the first few notes of "As Time Goes By" 
 * from the movie Casablanca.
 */
public class AsTimeGoesBy extends OldSong {
  public AsTimeGoesBy() throws MidiUnavailableException, 
                               InvalidMidiDataException {
    super(65, 20, 8);
  }

  public void makeSong(int key) throws InvalidMidiDataException {
    addSilence(7);
    add(key);
    add(key + 1);
    add(key);
    add(key - 2);
    add(key - 4);
    add(key - 2, 3);
    add(key);
    add(key + 3);
    add(key + 1);
    add(key);
    add(key - 2);
    add(key + 1, 3);
    add(key + 3);
    add(key + 8);
    add(key + 7);
    add(key + 5);
    add(key + 3);
    add(key + 5, 4);
    add(key + 2, 4);
    add(key + 3, 2);
    addSilence(1);
    add(key + 7);
    add(key + 10);
    add(key + 8);
    add(key + 7);
    add(key + 5);
    add(key + 7, 2);
    add(key + 8, 2);
    add(key + 3, 2);
    add(key + 3, 2);
    add(key - 4, 2);
    add(key - 2, 2);
    add(key - 4, 2);
  }
  public static void main(String[] args)
                   throws MidiUnavailableException, 
                          InvalidMidiDataException {
    OldSong asTime = new AsTimeGoesBy();
    asTime.start();
  }
}

In the private methods we have made the assumption that we have the locks for this. You can see that we have not synchronized the private methods due to this assumption. Someone editing the class would have to be very careful to read the comment and to understand what a "monitor" is. See my newsletter on "Why I don't read your code comments ..." to see why I think having assertions in comments is dangerous.

However, how can be assert that we have the lock inside the method? Thanks to Joshua Bloch, who told Bruce Eckel, who told me, we now have a Thread.hasLock() method. According to the writer of holdsLock(), it is only intended to be used for assertions, and is not there for performance improvements.

  /**
   * A piano teacher once told me that the first note in a bar 
   * should be emphasized.
   */
  private int volume() {
    assert Thread.holdsLock(this);
    assert pos != 0;                            
    return ((pos % resolution) == 0) ? 100 : 70;
  }

  private void addStartEvent(int note)
                      throws InvalidMidiDataException {
    assert Thread.holdsLock(this);                            
    ShortMessage message = new ShortMessage();
    message.setMessage(ShortMessage.NOTE_ON, 0, note, volume());
    track.add(new MidiEvent(message, pos));
  }

  private void addStopEvent(int note)
                     throws InvalidMidiDataException {
    assert Thread.holdsLock(this);                            
    ShortMessage message = new ShortMessage();
    message.setMessage(ShortMessage.NOTE_OFF, 0, note, 0);
    track.add(new MidiEvent(message, pos));
  }

We now have to compile the files with the -source 1.4 tag, like so:

javac -source 1.4 *.java

The assertions will be disabled by default. To enable them, we use the -ea switch, like so:

java -ea AsTimeGoesBy

Non-Reentrance

A lot of deadlocks occur because the code is reentrant, meaning that the thread acquires the same lock twice. You could use assertions to check that you have not got a lock before entering a synchronized block, like this:

public class A {
  // ...
  public void f() {
    assert !Thread.holdsLock(this);
    synchronized(this) {
      // ...
    }
  }
}

Thanks to Bruce Eckel for telling me about Thread.holdsLock().

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...