The Keykos kernel uses exclusive locks to keep more than one processor from making changes in some particular locked area of memory at once. It also uses exactly the same locks and lock commands to keep even one processor from making changes in some particular locked area of memory at once under two guises. Consider a primitive to swap parts of two arrays. References to the arrays are parameters of the primitive. Even when the primitive is presented with the same array twice it is possible to do the right thing but not without noticing the coincidence. The kernel’s style is to rely on the failure to get the second lock as the clue to look for the coincidence. This is not only efficient but the coincidence test is unlikely to be overlooked.
I just learned that the Java lock mechanism considers there to be no conflict when the same thread siezes one lock twice. I bet that there are Java programs that take object references as parameters, lock both objects and fail to test for coincidence, and then do the wrong thing for identical references.
I suppose that one reason Java does this, is so that code in a synchronized method in the definition of a Java class can invoke, directly or indirectly, synchronized methods in the same instance. The count kept by the lock mechanism for the instance will count the number of stack frames for synchronized methods of this instance. The Java semantics is to block on an unavailable lock and automatically switch to another thread with no opportunity for the application to respond to the collision. The kernel trick works because it is in charge of its own task management. If indeed the array is locked and it is not a coincidence of arguments, then it puts down that task and finds a less contested thing to do. This plan for allowing one thread to own a lock more than once seems error prone to me.
This confirms my bias that the place to do mutual exclusion is when control first enters code that defines some particular instance. Subroutine calls can be done within the object without going again thru the front door with the attendant lock overhead. By contrast, Java goes thru locking logic each time code calls another method even in the sense of a local subroutine. In Keykos each domain is an exclusive lock and code within the domain can use ordinary subroutines, circa 1950. The lock is released when the domain becomes available.
I suppose you can get this effect in Java by exporting public synchronized methods which call private unsynchronized methods. Internal invocations use these and don’t go thru the front door. I would feel happy about this style if I were warned of any multiple seizures of a lock, even by the same task.
Per Brinch Hansen has found other deficiencies in Java synchronization in his note: Insecure parallelism with Java?.