我对Java中同步代码块的行为感到困惑。我观察到有一种行为我只是不明白。考虑以下代码(由于结构相同,我将重用我问的另一个问题here的代码示例):
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
}
}
}
}
}
这种方法可能看起来很复杂。请注意,我是从一个Android示例应用程序改编而成的,这个问题不是关于重新设计代码结构的问题,除非有必要解决此问题。请记住,线程中的
while
循环是一个绘图循环,并且有时我的游戏逻辑会出现并调用modifyFoo
方法(Android SDK用户可能将此视为对LunarLander示例的修改)。对modifyFoo的调用如下所示:Log.d("activity", "calling modifyFoo");
modifyFoo();
当我调用该方法时,意外的行为显示在日志输出中。我期望这样的事情:
thread: loop
thread: loop
thread: loop < `modifyFoo` method called during this loop iteration
activity: called modifyFoo
outer: outer method
thread: loop
thread: loop
但是我看到的结果更像这样(编辑:复制带有时间戳的实际日志):
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
请注意,在循环中的同步块的开头和结尾处添加其他日志语句可确认在执行该块时正在调用
modifyFoo
方法。对
modifyFoo
的调用与服务之间的空间(线程循环迭代的次数)可能会非常长,但有时,延迟是如此之短,以至于它不明显(实际上似乎是随机的)。由于外部方法在我的UI线程上运行,因此它表现为冻结的UI。显然,这是一个问题。在线程循环中的同步块之外插入1ms Thread.sleep(1)
似乎可以解决问题,所以我的最初想法是“哦,while循环中的同步块没有给外部线程足够的时间来调用ModifyFoo方法”。但是,根据某些日志记录,似乎并没有在此延迟期间始终执行modifyFoo
,这正是我所期望的。所以问题是:这是怎么回事?一旦外部UI线程在
modifyFoo
中的同步块处等待,为什么在modifyFoo
使它“脱离机翼”之前,内部线程会重新获得锁?无法同步阻止“自我排队”? 最佳答案
每当任何synchronized
完成执行时,它都会通过说通知所有其他线程等待同一对象上的锁定。
“我将释放对此对象的锁定,任何人都可以开始执行。”
现在,将为哪个线程提供锁定的选择是随机的(我想说是未知的)。
在您的示例中:
可以说正在执行线程循环并调用了modifyFoo
方法。
正在运行modifyFoo的线程将等待直到锁释放,因为while循环释放了锁,因为while循环将另一个线程放入等待同一对象的锁,并选择其中任何一个。现在您的ModifyFoo仍在等待,直到此循环完成并且再次发生相同的事情。
因此,在执行了几次线程循环(将是随机的)之后,修改foo gets和执行机会。