Java 为什么我的同步代码块似乎忽略了同一对象上另一个正在等待的同步代码块?

Java 为什么我的同步代码块似乎忽略了同一对象上另一个正在等待的同步代码块?,java,android,thread-synchronization,Java,Android,Thread Synchronization,我对Java中同步代码块的行为感到相当困惑。我观察到一种行为,我就是不明白。考虑下面的代码(我将重用另一个问题的代码示例,因为结构是相同的): 这种方法可能显得错综复杂;请注意,我是根据一个Android示例应用程序改编的,这个问题不是关于重新设计代码的结构,除非这是解决问题所必需的。请记住线程中的while循环是一个绘图循环,我的游戏逻辑有时会调用modifyFoo方法(Android SDK用户可能会认为这是对LunarLander示例的修改)。对modifyFoo的调用如下所示: Log.

我对Java中同步代码块的行为感到相当困惑。我观察到一种行为,我就是不明白。考虑下面的代码(我将重用另一个问题的代码示例,因为结构是相同的):

这种方法可能显得错综复杂;请注意,我是根据一个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
调用该方法时,日志输出中将显示意外行为。我期待这样的事情:

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
请注意,在循环中的同步块的开头和结尾添加额外的日志语句可以确认在执行该块时正在调用
modifyFoo
方法

调用
modifyFoo
和服务之间的间隔(线程循环迭代次数)可能非常长,但有时延迟很短,以至于无法察觉(实际上,它似乎是随机的)。当外部方法在我的UI线程上运行时,它显示为一个冻结的UI。显然,这是个问题。在线程循环中的同步块之外插入1ms
线程。sleep(1)
似乎可以解决问题,因此我最初的想法是“哦,while循环中的同步块没有给外部线程足够的时间来调用modifyFoo方法”。然而,根据一些日志记录,似乎并不是总是在这个延迟期间执行
modifyFoo
,这是我所期望的


所以问题是:这里发生了什么?一旦外部UI线程在
modifyFoo
中的同步块处等待,为什么内部线程在
modifyFoo
使其“脱离翅膀”之前重新获得锁?无法同步阻止“自队列”

每当任何
已同步
完成其执行时,它会通知等待同一对象锁定的所有其他线程,方法是说

“我正在释放此对象上的锁,任何人都可以开始执行。”

现在选择哪个线程将被赋予锁是随机的(我会说未知)

在您的示例中:

假设线程循环正在执行,并且调用了
modifyFoo
方法

运行modifyFoo的线程将一直等待,直到锁被释放。现在,由于while循环,锁被释放的那一刻,另一个线程被放入同一对象的wait for lock中,并拾取其中任何一个。现在,您的modifyFoo仍在等待,直到这个循环完成,同样的事情再次发生


因此,在执行了几次线程循环(这将是随机的)之后,修改foo get和执行的机会。

这里要记住的是,一旦一个线程阻塞等待一个关键部分释放,那么就由调度程序决定何时屈服,而不需要任何其他代码告诉其他线程如何操作,例如
wait()
notify()
,调度程序没有义务“友好”,请原谅这个双关语。看


早在Java 1.5之前的时代,使用
synchronized/wait/notify
是必要的,但在带有
Java.util.concurrency
包的现代代码中,建议使用以下并发范例,以降低锁定错误的可能性并避免死锁或饥饿:

为什么要在某个随机对象上同步?如果要在某个对象上实际同步,请尝试使用该对象实例。否则,您需要同步该方法(或在块级别)。如果你想要一个锁,可以使用一个@ElliottFrisch在实际源代码中,该对象是Android SurfaceHolder;我正试图尽可能地匹配Android SurfaceView示例。请注意,我只有一个对象实例(内部类和外部类都维护对它的引用,但只有一个实例;据我所知,将对对象的引用维护为类中的字段不会创建同一类的新实例).@alfasin契约完成了,答案确实是while循环的结束和开始之间的JMP实际上是瞬时的?没错。在线程池中为等待对象的线程发送通知并选择一个对象并不是那么即时的。我想现在我把它看作一条指令,这很有意义。我想我是在数学上想得太多了,而不是系统的实际工程!