Java Puzzle 1: Clowns - Solution
Feb 18, 2012Spoiler warning: If you want to find out how you can fit 20 clowns into a Volkswagen, read on. If you haven't seen or tried solving the clowns java puzzle yourself, do that first!
As a reminder: here's the code it's all about:
Path to the solution
Imagine this was a real car with real clowns.
-- It's impossible to get twenty clowns in! There are only five seat belts, and this Volkswagen really insists on safety. When a clown enters, it first checks if there aren't already five of them sitting in it. Only after that checks it allows another one to sit (clowns.add(clown)
). As soon as there are five clown sitting, there's no way to pass the check anymore, so no way to add any extra clown.
-- You're right that we can't add them one by one. But there is a window of opportunity between the check when they enter the car, and when they really sit down. Make all twenty clowns simultaneously enter the car. They will all pass the check of the Volkswagen, because it only counts clowns that are already sitting down. And only after the check let them sit.
-- The car's synchronized
protection prevents anyone else from doing anything with it between the check and the sitting down.
-- Indeed, it prevents someone else from interfering. It doesn't prevent the Volkswagen or the clown itself to do it if they're given the opportunity (on the same thread).
-- But they don't get that opportunity; the car is a control freak. After the check it immediately makes them sit in their seats (in the Set
).
-- Did you have a look at those seats? They're numbered, and they always first asks a person at which number they want to sit (.hashCode()
). We've got some weird clowns here: when they're given the opportunity to decide that, right before answering, they can quickly pull another clown into the car. And that clown then does exactly the same with another clown. That goes on until there's a stack of twenty of them recursively pulling each other in. As long as their ass doesn't touch the seat, the seat-belt sign won't come on.
-- That must be quite a sight.
The solution
Call Volkswagen.add
recursively, by overriding Clown.hashCode()
:
And the winner is...
The first solution came from Heinz Kabutz, famous from The Java Specialists' Newsletter. He actually pushed me to publish these puzzles in the first place and promoted them. To show my infinite gratitude I'm going to disqualify him for having seen the puzzle in advance.
I received 7 correct solutions that passed the clarified rules. All used the same principle: adding them simultaneously using hashcode. Half of them also danced around the Volkswagen a bit in different threads to accomplish that. That's fine; there's no ban on dancing clowns here.
Congrats to all of you, especially Manuel Alvarez who was the first, and runner-up Jean-Louis Willems with the first short solution.
Conclusion
A simple check at the beginning of a synchronized method is not enough to guarantee safety if you're not very careful with which code you give control to. But should you really worry about crazy recursive clowns when writing your simple Volkswagen code?
No, you shouldn't. Well, not unless it's about privileged code handling a sandbox -- such as code of the JDK itself when running an applet. But it sure is interesting to explore these dark corners of Java. We'll continue on that track soon with a puzzle about dreams...
11 comments
Hello,
My solution here: http://pastebin.com/BAzH0QAP
I make 20 threads calling the add method. But as the method is synchronized on Wolkswagen i use hashcode (called by the HashSet.add) to put a wolkswagen.wait so that other threads can acquire the wolkswagen lock and enter the Wolkswagen.add method. Thus i make all threads waiting in that HashSet.add method, and when they are all there i release all the threads so that all the clowns can be added to the Set.
This works because the hashCode method is called before the set size is incremented
My different solution:
Haha.. brilliant. I had to ‘cheat’ to solve it!
Awesome ! I’m eager to try more of thoses.
Can this also be done by overriding the equals method and putting a similar hack?
Yes, if you also override hashcode so that it returns the same (e.g. “return 1”). Otherwise the equals method won’t be called.
Hi, Thank you for the puzzle. I really hope this is not that obvious, but here it is. The first thing, I broke a rule - but I still found this quite interesting.
The rule I broke was adding a class to the package clowns. I did not change any of the provide files (note even You). The class I added was the following.
Because the Volkswagen class uses the import java.util.*; and not import “java.util.HashSet;” the Hashset in the same package is used :) Thus solving (but cheating) the problem.
I should have been more careful with that import statement indeed!I did think of that, and removed thejava.util.*
import. But it looks like an older version went into the Specialists’ Newsletter. Nicely spotted though!Saturday night and nothing to do, might as well catch up on JavaSpecialists newsletters…
Thank you Wouter for such a fantastic puzzle and Heinz @ JavaSpecialists for encouraging publishing.
I was initially thinking about the dancing-clowns approach, remembering that Object.wait() would release the lock. But then I considered the recursive add technique. I grabbed a keyboard and crowded that beetle.
Mine was not quite as elegant, but still hit the mark.
@Reg … uber sneaky jamming your own HashSet into place.
I heard about this from the Java Specialists Newsletter. My solution is not as elegant as the RecursiveClown, but uses 20 threads to get every clown into the car before sitting down.
Couple of requests:
I wonder if there is a way to prevent you from adding more than 5 clowns?!
I think there is one. You could prevent the add method from being called from within itself with a boolean class variable in VW. Or you could use your own counter instead of using the hashset’s. Just be sure to increment it immediately, and decrement it if the clown was not added.
If you don’t want people to just overwrite the methods in Volkswagen, you should make this class final. Otherwise simply overwriting the methods without the CAPACITY check will do the job too:
The rules say:
Your subclass of Volkswagen clearly contains a copy of the line. When it is executed the real goal line in the Volkswagen class is never reached.
Btw, making Volkswagen final doesn’t change any of that. Then you just make your volkswagen not extend it and you can still just as easily reach your copy of the line. Going further down that path, you can just write this:
And that’s obviously not a solution either.
If there was List instead of Set, would there be any solution?