Friday, March 2, 2012

Find the bug - part two.

Nico, a wonderful idea! Here's my contribution:

The most complicated thing in the world to understand is concurrency. Not even Einstein's General Theory of Relativity comes close. Here is an interesting gem that I got from a newsletter quite some time ago. I found the puzzle incredibly interesting and it convinced me to NEVER EVER EVER EVER:
  1. Assume I understand concurrency.
  2. Use the old core Threading stuff rather than the new modern concurrency frameworks (i.e. Threading pools, etc.).
  3. Assume I understand concurrency.
  4. Skip testing threaded applications - because I'm clever, it makes sense... and besides I understand concurrency.
  5. Assume I understand concurrency.
Oh, I almost forgot, the most important rule is to never assume I understand concurrency (not even at a job interview at google)!

Look at the following Rorotika-inspired class and tell me what happens. Keep careful track of all the delays.
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 {
System.out.println("..working!");
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 {
System.out.println("Ok guys! Have a beer!");
quittingTime = true;
join();
}
// Rescind quitting time - Called by evil boss
synchronized void keepWorking() {
System.out.println("Work! Dammit! I don't "+
"pay you to drink beer!");
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();
}
}