Java 使用linelistener等待()和通知()

Java 使用linelistener等待()和通知(),java,Java,由于这是我的第一篇文章,我为它的结构中的任何错误道歉。我正在做一个个人项目,这是一个用java制作的时钟,可以显示时间/日期/天,每季度播放一个音频文件,还可以报时。GUI是用swing项构建的 当时钟到达一定时间时,它必须发出蜂鸣/播放声音,我希望在时钟响起时GUI上显示一个图标。此时,图标在方法开始时切换,在方法结束时再次切换。在第二次切换图标之前,我希望该方法在播放音频时wait()。(也许有更好的方法可以做到这一点。) 我对java缺乏经验,感觉好像我使用了wait()和notify()

由于这是我的第一篇文章,我为它的结构中的任何错误道歉。我正在做一个个人项目,这是一个用java制作的时钟,可以显示时间/日期/天,每季度播放一个音频文件,还可以报时。GUI是用swing项构建的

当时钟到达一定时间时,它必须发出蜂鸣/播放声音,我希望在时钟响起时GUI上显示一个图标。此时,图标在方法开始时切换,在方法结束时再次切换。在第二次切换图标之前,我希望该方法在播放音频时wait()。(也许有更好的方法可以做到这一点。)

我对java缺乏经验,感觉好像我使用了wait()notify()不正确。我一直在看例子,直到我头晕目眩,我仍然不确定我需要的解决方案

我正在使用LineListener通知剪辑已播放完毕,可以继续切换图标

以下是播放钟声的方法:

public void play() throws InterruptedException {
    while (mute == false) {
        synchronized (chimeLock) {
            ChimeClockGUI.toggleChimeIcon();
            clip.start();
            do {
                chimeLock.wait();
            } while (clip.isActive());
            ChimeClockGUI.toggleChimeIcon();
        }
    }
}

最后,来自LineListenerupdate()方法:

@Override
public void update(LineEvent le) {
    synchronized (chimeLock) {
        LineEvent.Type type = le.getType();
        if (type == LineEvent.Type.OPEN) {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .format(new Timestamp(System.currentTimeMillis())) + ": Clip opened...");
        } else if (type == LineEvent.Type.CLOSE) {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .format(new Timestamp(System.currentTimeMillis())) + ": Clip closed...");
            chimeLock.notifyAll();
        } else if (type == LineEvent.Type.START) {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .format(new Timestamp(System.currentTimeMillis())) + ": Clip started...");
        } else if (type == LineEvent.Type.STOP) {
            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                    .format(new Timestamp(System.currentTimeMillis())) + ": Clip stopped...");
            chimeLock.notifyAll();
        }
    }
}
我包括调用play()toll()的方法

chimeService.scheduleAtFixedRate(() -> {
    try {
        ChimeClockSound.TOLL.toll();
        } catch (InterruptedException ex) {
            Logger.getLogger(ChimeClockGUI.class.getName())
                  .log(Level.SEVERE, null, ex);
    }

如有任何帮助或建议,将不胜感激。谢谢大家!

这里的第一个问题是
clip.isRunning()
返回
false
,直到数据实际开始加载,因此您应该确保总是使用do while循环调用
wait()

do {
    wait();
} while (clip.isRunning());
最好在专用锁对象上调用
wait
/
notify
,而不是在类的整个实例上。通过这种方式,
wait
/
notify
的其他用法不会产生干扰,并且您的方法不需要进行
同步
,这会降低速度

private final Object chimeLock = new Object();
使用此选项,您可以将
wait()
替换为:

synchronized (chimeLock) {
    chimeLock.wait();
}
synchronized (chimeLock) {
    chimeLock.notifyAll();
}
…并将
notifyAll()
替换为:

synchronized (chimeLock) {
    chimeLock.wait();
}
synchronized (chimeLock) {
    chimeLock.notifyAll();
}
…并从方法声明中删除
synchronized
,因为它现在正在锁对象上同步


最后要记住的是,只有当线程被中断时才会抛出
InterruptedException
,这通常意味着(比如JVM)希望线程停止运行。当你发现这个问题时,你应该打包并尽快返回。

注意
synchronized
函数会阻止当前(这个)对象,没有任何其他线程可以访问它。如果对象/类与GUI相关,则不建议锁定
,只需使用
对象
。在这种情况下,
play()
toll()
仅从单个
ScheduledExecutor
线程调用。感谢您在这方面的帮助。我已经实现了您的建议,但在调用
chimeLock.notifyAll()
时似乎什么都没有发生。您是否从方法中删除了
synchronized
?您的代码现在看起来像什么?另外,我刚刚注意到,如果剪辑从未在切换块中启动,您需要确保
toll
不会进入do-while循环,因为
update
将永远不会被调用。我已更新了我的问题以反映更改。从理论上讲,当叫toll时,应该始终播放剪辑。另外,我添加了调用
play()
toll()
的方法,以防我在那里做错了什么。我注意到您正在使用第二个clip实例
。clip
toll
方法中,您是否也添加了这个类实例作为该片段的行侦听器?这是它调用
update
的唯一方法,它调用
notifyAll
。还要注意,如果要多次播放同一个剪辑实例,需要在再次调用
start
之前使用
Clip.setFramePosition(0)
重置它。这将影响
播放中的
while
循环以及对
收费的多个调用。最好将所有这些分割成一个私有方法,该方法在锁上同步、播放片段、等待锁并重置片段。