Thursday, November 20, 2008

Time's Up

I'm certainly not the first person to say this, but it bears repeating, using Timer in the post Java 5 world is not a good idea. I got bitten by Timer's lack of exception handling again recently. If you must use a Timer and are relying on it to be long-lived, make sure the run() methods on your tasks can't throw any unchecked exceptions, because if any exceptions manage to leak out of the run() method, the timer thread will terminate and no more tasks will run. I used to use Timer in situations where I wanted to be able to cancel tasks, since TimerTask exposes a cancel() method. What I failed to realize was that you can do the exact same thing using ScheduledExecutorService using the ScheduledFuture object that is returned when one of the schedule methods is invoked. It's a bit more cumbersome to cancel tasks this way since you may have to keep references to both the task object and the future object, but it wouldn't be too difficult to create a custom class to manage this.

Monday, November 10, 2008

Who's the Boss?

JBoss Cache 3.0.0 is almost ready, and its big feature, multi-version concurrency control (MVCC), is a big improvement. I assume that anyone who has tried to do anything interesting with JBoss Cache has run into this limitation: any time you try and upgrade a read lock to a write lock while another thread is holding a read lock on the same node, you get an UpgradeException. Preventing this situation from arising in an application is very complicated. You can use Java synchronization to prevent concurrent access to the same data, but then you're throwing away JBoss Cache's fine-grained locking system. The solution that I settled on was a fine-grained (but not hierarchical) java.util.concurrent.locks.Lock-based layer that prevented concurrent access to the same cache nodes. JBoss Cache 3.0.0 makes all of this code unnecessary. In a pinch me, I'm dreaming moment, I wrote this test (adapted from the TxDeadlockUnitTestCase) that causes the JBCACHE-97 bug and ran it against a 3.0.0 CR3 and 2.1.1 GA just to make sure the problem is really gone.

Saturday, September 27, 2008

Can't Hardly Wait

I ran into a wait leak issue recently and it made me wonder if there was anything I could to do prevent this issue from happening again. I found this article from a few years back written by Java performance gurus Jack Shirazi and Kirk Pepperdine. The article mainly discusses tactics for detecting wait leaks. When it comes to preventing them from happening in the first place, there's no silver bullet. Something that I thought of that wasn't mention in the article is preferring the timed wait() method instead of the no-arg version in order to prevent wait leaks. Obviously, it's not always possible to specify a wait timeout, but in situations where it is possible, it can help an application recover gracefully from a wait leak.

Tuesday, September 23, 2008

Heads and Tails

For whatever reason, tail recursion is a concept I could never quite wrap my head around. It was either something that I learned and then forgot, or heard about and never completely understood. I was reading this posting about tail recursion, Scala, and the JVM and something in my brain just clicked and it makes total sense to me now. It should come in handy when I finally get around to learning Scala.

Thursday, September 18, 2008

Compared To What?

I recently ran into an issue with the venerable Collections#sort(List) method. The List that I was sorting had an unexpected null element, which resulted in a NullPointerException during the sort. My first inclination was to change the compareTo() method to handle nulls. This didn't work, however, because of line 1157 of the Arrays#mergeSort(Object[], Object[], int, int, int) method.

1153           // Insertion sort on smallest arrays
1154           if (length < INSERTIONSORT_THRESHOLD) {
1155               for (int i=low; ilow &&
1157                            ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
1158                       swap(dest, j, j-1);
1159               return;
1160           }

The method doesn't do any null checking, so if the object on the left side of the comparison is null, a NullPointerException will ensue. Punting on this concern was probably a good idea, since the whole idea of allowing null objects in a List is controversial, nevermind figuring out where to put them in the sort order. It would have been nice if there was more information about this in the API documentation. Effective Java mentions that the compareTo() method of a Comparable type should throw a NullPointerException if it receives a null object in Item 11, but the API docs for Arrays and Comparable don't say anything about how nulls are handled.

I looked further into the Arrays class and noticed that the Comparator and Comparator-less sort methods are essentially line-by-line copies of each other, except the Comparator version uses the Comparator object to perform the comparison. I would have expected them to use the same underlying code, perhaps by creating an anonymous Comparator instance that delegates to the compareTo() method, but I digress...

I finally solved the problem by creating a Comparator that can properly handle null objects. If you ever face this problem and you're using the Google Collections library, you can use the Comparators#nullLeastOrder() or Comparators#nullGreatestOrder() methods to obtain a Comparator that can handle null objects gracefully.

Thursday, September 4, 2008

The Joy of Hex

The Java platform is so large that even experienced developers discover new classes and methods nearly every day. My discovery du jour is the 2-arg form of java.lang.Integer's parseInt() method. It's a pretty slick way to parse a hex dump, among other things.