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.
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:
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
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
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)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.