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"?