|
The Java Specialists' Newsletter
Issue 144 2007-05-16
Category:
Book Review
Java version: 1.4+ Book Review: Java Puzzlersby Dr. Heinz M. KabutzAbstract:
Experienced Java programmers will love the Java Puzzlers
book by Josh Bloch and Neal Gafter, both well known Java
personalities. In this newsletter, we look at two of the
puzzles as a teazer for the book.
Welcome to the 144th edition of The Java(tm) Specialists' Newsletter, sent to you from the
Island of Crete. The last 6 months of living here on Crete
have cost me about 4 years. When I arrived here, I looked
38, now people are starting to guess my age at 34. Perhaps
by 2010, they will start asking me why I am not in school :-)
I can strongly recommend spending 2 weeks here on holiday
this summer and if you do, please look me up in Chania. A
number of "Java Specialists" subscribers have already done
this and we typically ended up lunching at the quaint little
beach restaurant at
Kalathas.
As from July 2007, I will be offering Java code reviews
for your team to get an expert's viewpoint of your Java
system.
Over the years, I have done several Java code reviews at
various companies. Often Java developers were apprehensive
at the thought of having a Java expert look at what they had
produced. Imagine how pleasantly surprised they were when
I complimented them on what they had produced (not always)?
The code review gives me an opportunity to then present Java
code and design improvements that help to make your Java
system easier to maintain in future. This leads to big cost
savings that quickly exceed the initial outlay for the code
review.
Upcoming Java Specialist Master Courses:
- please click here to sign up.
As from May 2010, we are also offering this course on the island of Crete. We
only accept 6 students per class in Crete, due to the size of our conference
room. Please book early to avoid disappointment!
San Jose CA, Mar 16-19 2010, $3500 Ottawa, Canada, Mar 22-25 2010, $3500 Oslo, Norway, Apr 13-16 2010, Kr 24500 Montreal, Canada, Apr 20-23 2010, $3500 Toronto, Canada, May 17-20 2010, $3500 Chania, Crete, May 25-28, Jun 29-Jul 2 or Aug 24-27 2010, €2500
In-house courses if these dates or locations do not suit you - click here for more information.
At the Sun Tech Days in London (March 2007), I was suckered
into participating in a Java Black Belt
competition. I studiously avoid public contests like
that, since I do not want to be upstaged by a younger,
smarter participant. On my first attempt, I scored 4/5. For
some reason, seeing my name on top discouraged some
worthy contestants from even trying, which made me win the
first round by default. All that they had to do was score 5/5 and I would
have come second or third. The questions were quite
interesting though, and I would recommend giving it a try if
you see their stall at one of the Java conferences.
As a prize, I got to choose a book from a wonderful display
of Java books. I had paged through the Java Puzzlers book before at
the Sun Tech Days in Johannesburg and of course, had heard a
lot about it. Whenever I published really obscure ideas,
readers of The Java(tm) Specialists' Newsletter would point me to the Java Puzzlers book,
so this was an obvious choice.
Authors Dr Joshua Bloch and Dr Neal Gafter are fellow
Java Champions.
They work for Google, but used to be employed by Sun
Microsystems. Joshua Bloch wrote a large chunk of code in
the JDK and Neal Gafter was working on the Java compiler.
They are exceedingly smart and each has a PhD in Computer
Science.
I started reading the book whilst on the treadmill at the
local Greek gym, which opens at 10:00 and closes between
13:00 and 16:00. The only gym in the world with those hours.
Since I was a bit distracted, I flunked a few of the easier
questions at the beginning of the book.
Here is a warning. Make sure that you spend a few minutes
on each puzzle before attempting the answer, otherwise it
has a good chance of being incorrect.
Overall, out of 95 questions, I got 69 correct, 4 partly
correct and 22 completely wrong. I would love to hear of
any readers who got 95 questions correct (Joshua and Neal,
you're exluded, ok)! If you let me know, I will post your
name in the next newsletter as a token of fame.
You might be wondering to yourself - why is Heinz even
reviewing this book, which is already on the bestseller list
and which we all own? Well, it took a Java Black Belt
competition to motivate me to get hold of this book, and if
you are like me, you might need additional inspiration to put
down the $28.79 .
There are two puzzles that I would like to discuss in this
newsletter. The first involves throwing checked exceptions
in an unchecked fashion. The second involves some threading
and locking issues. Warning - contains spoilers!
Puzzle 43: Exceptionally Unsafe
This puzzle asks us to find at least two ways to throw
exceptions, circumventing the exception checking. They give
this as a third option:
// don't do this - circumvents exception checking!
public static void sneakyThrow(Throwable t) {
Thread.currentThread().stop(t);
}
I must admit, this one had me stumped. I did not know how
we could throw checked exceptions without the compiler
picking it up and without doing the compile and switch trick.
Joshua and Neal gave clues that it was possible to do this
using no deprecated methods and another way using Java 5
features.
The first approach uses a deficiency in the
Class.newInstance() method. The
Constructor.newInstance() method converts any
exceptions to an InvocationTargetException, but the
Class.newInstance() simply passes the exception
along. Here is their approach:
public class Thrower {
private static Throwable t;
private Thrower() throws Throwable {
throw t;
}
public static synchronized void sneakyThrow(Throwable t) {
Thrower.t = t;
try {
Thrower.class.newInstance();
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InstantiationException e) {
throw new IllegalArgumentException(e);
} finally {
Thrower.t = null; // avoid memory leak
}
}
}
We need a no-args constructor to exploit this
weakness in Class.newInstance(), which is why
we use a static variable to keep the actual throwable
instance. This is how we would call the
sneakyThrow() method in our code:
public void diddleDum() {
System.out.println("Oh I'm so innocent");
IOException exception = new IOException("hehe");
Thrower.sneakyThrow(exception);
}
One of the limitations of this solution is that if you try to
throw InstantiationException or
IllegalAccessException, these will be caught in
the sneakyThrow() method and will thus cause an
IllegalArgumentException instead.
Instead of synchronizing statically, we could also use
ThreadLocal to achieve a similar except, but without blocking
all threads calling this method. Here is how you could do
that:
public class ThrowerConcurrent {
private static ThreadLocal<Throwable> throwables =
new ThreadLocal<Throwable>();
private ThrowerConcurrent() throws Throwable {
Throwable throwable = throwables.get();
throwables.remove(); // avoid memory leak
throw throwable;
}
public static void sneakyThrow(Throwable t) {
throwables.set(t);
try {
ThrowerConcurrent.class.newInstance();
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InstantiationException e) {
throw new IllegalArgumentException(e);
}
}
}
The lesson to learn from this code is that you need to be
aware that Class.newInstance can throw
checked exceptions that it does not declare.
The next approach uses generics to achieve the same effect,
although in a completely different approach. For maximal
compatibility, generics are implemented by type
erasure: Generic type information is checked at
compile but not at run time. We all know this already,
but this approach exploits that to throw checked exceptions:
public class TigerThrower<T extends Throwable> {
public static void sneakyThrow(Throwable t) {
new TigerThrower<Error>().sneakyThrow2(t);
}
private void sneakyThrow2(Throwable t) throws T {
throw (T) t;
}
}
The compiler warns you about the throw (T) t;
since this is an unchecked cast. This warning tells us that
the cast will not be checked at run time. Unchecked warnings
are dangerous, and should be eliminated from your code.
So now you know how to throw checked exceptions from a
context that does not declare them. This is just for
interest and has no practical application. It will
definitely cause me to raise an eyebrow, if I spot this
technique used during a code review
of your Java system!
Puzzle 77: The Lock Mess Monster
This puzzle was nicely obscured, and I walked into the trap
like a sheep led to slaughter. In the code, they do
something that I always try to avoid; they use
this as a lock to synchronize on.
I already mentioned this problem in in a newsletter written in 2001.
Consider this code - what is the output?
import java.util.*;
public class Worker extends Thread {
private volatile boolean quittingTime = false;
public void run() {
while(!quittingTime) {
pretendToWork();
}
System.out.println("Beer is good");
}
private void pretendToWork() {
try {
Thread.sleep(300); // Sleeping on the job?
} catch (InterruptedException e) { }
}
// It's quitting time, wait for worker -
// Called by good boss
synchronized void quit() throws InterruptedException {
quittingTime = true;
join();
}
// Rescind quitting time - Called by evil boss
synchronized void keepWorking() {
quittingTime = false;
}
public static void main(String[] args)
throws InterruptedException {
final Worker worker = new Worker();
worker.start();
Timer t = new Timer(true); // Daemon thread
t.schedule(new TimerTask() {
public void run() {
worker.keepWorking();
}
}, 500);
Thread.sleep(400);
worker.quit();
}
}
It would make most people scratch their head for at least a
few minutes. We start a worker thread that works - or at
least pretends to work - until quitting time. Then the
program schedules a timer task representing an evil boss who
tries to make sure that it's never quitting time. Finally,
the main thread, representing a good boss, tells the worker
when it's quitting time and waits for the worker to finish.
At first glance, it looks as if at the following times things
happen:
300ms The worker thread checks the volatile boolean
and continues working.
400ms The good boss (main thread), calls the
quit() method. It acquires the lock to
this, thus preventing other threads
from getting that lock. It then sets
quittingTime to true
and joins the worker thread, thus waiting for it to
complete.
500ms The evil boss now tries to call the
keepWorking()
method, but cannot because the main thread still owns the
lock to this.
600ms The worker thread checks the volatile boolean
for the second time, sees it is true
and quits, thus letting the main thread (good boss) also
complete. Since the evil boss is a daemon (thread), he
also dies.
Alas, that is not what happens. If you run the code, you
notice that the program just hangs up. A thread dump will
show you that the worker thread is still pretending to work
and the main thread is trying to complete the call to
join().
Why does this happen?
The call to join() is itself
synchronized and internally calls the
wait() method on the object. Since Worker
extends Thread, this
refers to the thread and to the worker at the same time.
The solution to this problem is to never synchronize on this
or on whole methods. Always use a separate lock object, or
even the new Java 5 locks.
In this "BetterWorker", I have changed the Worker to not
inherit from Thread (favour composition over inheritance)
and I use specific lock objects:
import java.util.*;
public class BetterWorker {
private volatile boolean quittingTime = false;
private final Object quittingTimeLock = new Object();
private Thread workerThread = new Thread(new Runnable() {
public void run() {
while (!quittingTime) {
pretendToWork();
}
System.out.println("Beer is good");
}
});
public void start() {
workerThread.start();
}
private void pretendToWork() {
try {
Thread.sleep(300); // Sleeping on the job?
} catch (InterruptedException e) { }
}
// It's quitting time, wait for workerThread -
// Called by good boss
void quit() throws InterruptedException {
synchronized (quittingTimeLock) {
quittingTime = true;
workerThread.join();
}
}
// Rescind quitting time - Called by evil boss
void keepWorking() {
synchronized (quittingTimeLock) {
quittingTime = false;
}
}
public static void main(String[] args)
throws InterruptedException {
final BetterWorker worker = new BetterWorker();
worker.start();
Timer t = new Timer(true); // Daemon thread
t.schedule(new TimerTask() {
public void run() {
worker.keepWorking();
}
}, 500);
Thread.sleep(400);
worker.quit();
}
}
The code is still not terribly clear and should be rewritten
entirely to represent what we are trying to achieve - to have
a nice cold beer!
Some of the lessons we can learn from this:
Don't assume anything about what a library class will or
won't do with locks.
If you need full control over a lock, make sure that no
one else can gain access to it.
The book is filled with information like this, which
experienced Java developers should know. Some puzzles are a
bit theoretical - not something that a programmer would do in
real life. Also, a good IDE with syntax highlighting should
immediately highlight some of the problems in the puzzles.
However, all in all, Java
Puzzlers belongs on your bookshelf, together with
Effective Java ,
Head First Design
Patterns, Java
Concurrency in Practice and Java Generics and Collections.
Don't forget to let me know if you got all 95 puzzles right -
I would love to discover such a programmer!
Kind regards
Heinz
Book Review Articles
Related Java Course
|