Good for you that you wrote an unit test! For future reference: You want to test all kinds of edge values, which means for numbers at least 0, -1, 1, MAX_VALUE, MIN_VALUE - that would've caught the second bug you missed :-)
The problem with your code is that: synchronized(l) {}
translates to: synchronized(Long.valueOf(l)){}
. valueOf
caches Longs in the range -128, 127 but even this is optional (the JLS only requires it for integers surprisingly!). Consequently as soon as your ids are larger than 127 your whole scheme falls apart.
Your second method is the way to go, but you cannot just make the method synchronized - that will just synchronize on this
, so doesn't guarantee atomicity for the static map.
Instead do something like this:
Object newLock = new Object();
Object oldLock = map.putIfAbsent(id, newLock);
Object lock = oldLock != null ? oldLock : newLock;
synchronized(lock) {
}