我有一些代码响应PropertyChangeEvent
。这个事件的问题在于它可以连续几次触发或根本不触发。无法预测此事件是仅触发一次还是多次,并且我无法控制此事件是如何触发的。我希望事件监听器只被触发一次。
我对这个问题的解决方案是使用我写的一个名为DelayedRunnable
的类。我将监听器包装在DelayedRunnable
中。首次触发事件时,监听器计划在一秒钟后执行 - 我任意决定的时间。事件的后续触发被忽略,直到一秒钟过去。以下是DelayedRunnable
的代码:
public class DelayedRunnable implements Runnable {
final Runnable runnable;
final int delayAmount;
final TimeUnit delayUnit;
final ScheduledExecutorService service = new ScheduledThreadPoolExecutor(1);
final Runnable executeRunnable = new ExecuteRunnable();
final AtomicBoolean scheduled = new AtomicBoolean(false);
public DelayedRunnable(Runnable runnable, int delayAmount, TimeUnit delayUnit) {
if (runnable == null)
throw new IllegalArgumentException("runnable == null");
this.runnable = runnable;
this.delayAmount = delayAmount;
this.delayUnit = delayUnit;
}
public void run() {
if (!scheduled.compareAndSet(false, true))
return;
service.schedule(executeRunnable, delayAmount, delayUnit);
}
class ExecuteRunnable implements Runnable {
public void run() {
runnable.run();
scheduled.set(false);
}
}
}
当我将侦听器包装在DelayedRunnable
中时,它只被调用一次。 DelayedRunnable
的一个附带好处是,侦听器代码不会在Swing EDT上执行,因为侦听器代码很昂贵。
但是我发现有时候调用者永远不会被调用,可能是由于DelayedRunnable
中的并发问题。当我重新启动应用程序时,监听器再次神奇地工作。正如人们所预料的那样,由于并发编程的本质,我无法重现DelayedRunnable
无法工作的情况。另外,一秒的时间限制完全是任意的。在某些计算机上,一秒钟可能还不够。在其他计算机上,一秒钟太长。没有简单的方法来设定时限。
我有两个问题:
有没有人知道DelayedRunnable
我无法看到的问题?
处理这个问题比使用DelayedRunnable
方法更优雅吗?
答案 0 :(得分:2)
你的一个可运行程序可能会被挂起,因此将来的任何执行都会被阻止。
答案 1 :(得分:0)
AWT事件侦听器将在其他侦听器之前执行,因此您可以先在那里过滤事件。有类似的东西:
something like Container c;
.
.
.
c.addAWTEventListener(new AWTEventListener()
{
public void eventDispatched(AWTEvent e)
{
if(! e instanceof YOUR_EVENT_TYPE)
return;
//code to filter here
e.consume() //to remove if not in time bounds
}
});
请参阅http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/AWTEventListener.html了解用法
答案 2 :(得分:0)
正如jtahlborn所提到的,gui工作应该在事件队列中完成。确保所有gui工作都包含在EventQueue.invokeAndWait或EventQueue.invokeLater。
中实际上,根据事件生成方式的性质,您可以完全使用EventQueue.invokeLater
来避免延迟机制,因为要调用的runnable位于队列的末尾。如果在运行第一个回调之前将所有多个事件回调添加到队列,则可以确保使用invokeLater添加到队列的runnable将不会看到任何后续事件。然后,您可以使用类似于DelayedRunnable
中已有的标志,以确保只为第一个事件添加runnable。