线程在notifyAll()方法之前未锁定同步对象。

5

我希望有一个布尔值来通知系统中的某些部分,特定的服务已经启动。

出现了一些奇怪的错误:java.lang.IllegalMonitorStateException: object not locked by thread before notifyAll()

奇怪的是,notifyAll() 在一个同步块中被调用,这个同步块控制着我要调用 notifyAll() 的对象。

我的类开头是这样的:

public class MyService {

    public static Boolean notifier = Boolean.valueOf(false);

    @Override
    public void start() {
        synchronized (MyService.notifier) {
            MyService.notifier = Boolean.valueOf(true);
            MyService.notifier.notifyAll();
        }
    }

    @Override
    public void stop() {
        synchronized (MyService.notifier) {
            MyService.notifier = Boolean.valueOf(false);
            MyService.notifier.notifyAll();
        }
    }
    ...
}

我正在开发一款安卓应用程序。我认为这并不会影响任何事情,但我在问题后面添加了这个注释,以防它影响java的工作方式。

为什么在同步块内锁定对象时仍然出现异常?


1
请看这个SO问题 - S.R.I
3
因为监视器属于"对象(object)"而不是"字段(field)"。你正在对 Boolean.FALSE 进行同步,但是却在通知 Boolean.TRUE(或反之亦然)。 - Ian Roberts
4
故事的寓意是:不要锁定可变对象,确保它是“final”类型。你的代码比必要复杂得多,但如果没有看到你为此等待的内容,我无法建议用什么替换它(就目前而言,你可以将其全部删除)。我猜想你应该使用ExecutorService。 - Peter Lawrey
1个回答

17

这条线

MyService.notifier = Boolean.valueOf(true);

当你交换正在锁定的对象时,它会用一个新对象的引用覆盖该变量。因此,在进入同步块时获取锁定的对象与在调用notifyAll时使用的对象不同。所有notifyAll知道的是它没有获得被调用对象上的锁,这是在进入同步块后创建的新对象。

所有线程都需要使用相同的锁。就像Ian Roberts所说,锁属于对象。如果你覆盖了对象,你就有了一个新锁。


1
@J-Rou:很高兴能帮忙。每个人偶尔都会遇到困难,这是职业风险。 - Nathan Hughes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接