|
The Java Specialists' Newsletter
Issue 076 2003-08-05
Category:
Language
Java version: Asserting Locksby Dr. Heinz M. Kabutz
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.
Would you like to really understand Java concurrency? Join us for an
in-depth study of how threading works in Java. During the course,
you will learn how to write correct and fast multi-threaded Java code.
Please
click here if you would like to learn more. 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
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
Language Articles
Related Java Course
Discuss at The Java Specialist Club
|