I am rather puzzled about the behavior of synchronized code blocks in Java. There is one behavior I'm observing that I just don't get. Consider the following code (I'm going to reuse the code example from another question I asked here, as the structure is the same):
class Outer {
private Object lock;
private Foo foo;
public Outer() {
lock = new Object();
// The thread is actually started in an Android callback method,
// but I'll start it here as a demonstration
InnerThread thread = new InnerThread(lock);
thread.setRunning(true);
thread.start();
}
private void modifyFoo() {
synchronized(lock) {
Log.d("outer", "outer method");
foo.bar(); // Has some effect on foo
}
}
private class InnerThread extends Thread {
private volatile boolean running = false;
private Object lock;
public InnerThread(Object lock) {
this.lock = lock;
}
private void setRunning(boolean running) {
this.running = running;
}
@Override
public void run() {
while(running) {
// There is some timer management code here.
// It executes on the order of microseconds.
synchronized(lock) {
Log.d("thread", "loop");
foo.blah(); // Modifies foo
// Imagine some time intensive (milliseconds)
// drawing method calls here
}
}
}
}
}
This approach may appear convoluted; just be aware that I adapted this from one of the Android sample applications, and this question isn't about redesigning the structure of my code, unless that is necessary to fix the issue. Just bear in mind that the while
loop in the thread is a drawing loop, and that my game logic sometimes comes in and calls the modifyFoo
method (Android SDK users may recognize this as a modification to the LunarLander example). The call to modifyFoo looks like this:
Log.d("activity", "calling modifyFoo");
modifyFoo();
The unexpected behavior is shown in the log output when I call the method. I expect something like this:
thread: loop
thread: loop
thread: loop < `modifyFoo` method called during this loop iteration
activity: called modifyFoo
outer: outer method
thread: loop
thread: loop
But I see results more like this (EDIT: Copied actual log with timestamps):
01-23 04:34:28.303: D/thread(399): loop
01-23 04:34:28.335: D/thread(399): loop
01-23 04:34:28.350: D/activity(399): calling modifyFoo
01-23 04:34:28.366: D/thread(399): loop
01-23 04:34:28.381: D/thread(399): loop
01-23 04:34:28.413: D/thread(399): loop
01-23 04:34:28.428: D/outer(399): outer method
01-23 04:34:28.436: D/thread(399): loop
01-23 04:34:28.460: D/thread(399): loop
Note that adding additional log statements at the beginning and end of the synchronized block in the loop confirms that the modifyFoo
method is being called while this block is executing.
The space (number of thread loop iterations) between the call to modifyFoo
and the service can be extremely long, but sometimes, the delay is so short that it is unnoticeable (it seems, in effect, random). It exhibits itself as a frozen UI, as the outer method runs on my UI thread. Obviously, that's a problem. Inserting a 1ms Thread.sleep(1)
outside of the synchronous block in the thread loop seems to fix the problem, so my initial thought was "oh, the synchronous block in the while loop is not giving external threads enough time to call the modifyFoo method". However, based on some logging, it does not appear that modifyFoo
always executes during this delay, which is what I'd have expected.
So the question is: what is going on here? Once the outside UI thread is waiting at the synchronized block in modifyFoo
, why does the inner thread get back ahold of the lock before modifyFoo
makes it "out of the wings"? Cannot synchronized blocks "self queue"?
Whenever any synchronized
completes it's execution it notifies to all the other threads waiting for the lock on the same object by saying.
"I am releasing the lock on this object any one of you can start execution."
Now the choice of which thread will be given the lock is random.(I would say unknown).
In your example:
lets say thread loop is executing and modifyFoo
method is called.
The thread which is running modifyFoo will wait until lock is released now the moment lock is released because of the while loop another thread is put into wait for lock on the same object and any one of them is picked. now your modifyFoo is still waiting until this loop finishes and same thing happens again.
So after a few executions of thread loop(which will be random) modify foo gets and opportunity to execute.