【问题标题】:Invoke a code after all mouse event listeners are executed在执行所有鼠标事件侦听器后调用代码
【发布时间】:2009-11-25 22:06:30
【问题描述】:

我创建了一个面板,在该面板上绘制了一组对象。每个对象都作为鼠标事件侦听器添加到面板。据我所知,在事件发生后,会通知侦听器并且代码可能(或正在?)在多个线程中执行。是否可以附加一个将在所有侦听器完成执行其代码后执行的自定义代码?

【问题讨论】:

    标签: java event-listener


    【解决方案1】:

    在同一个线程(事件调度线程)中执行。

    要执行您想要的操作,您只需添加一个额外的侦听器并让该侦听器调用SwingUtilities 类的“executeLater”方法。

    要做的是等待 EDT 线程完成通知,然后调用您的代码。

    要对其进行测试,请添加此侦听器并查看它的作用:

     class MouseListener extends MouseAdapter {
         public void mouseClicked(MouseEvent e)  {
               // System.out.println("If uncommented this would be invoked with the rest of the listners");
               SwingUtilities.invokeLater( new Runnable() {
                    public void run() {
                        System.out.println("Invoked after all the listeners were notified");
                    }
                }
         }
     }
    

    当然,它是你所拥有的鼠标监听器。这个概念对所有其他听众都是一样的。

    【讨论】:

    • @czuk:如果特定的侦听器实现提前返回通知,并在单独的线程上“带外”执行工作,这种技术可能会引发问题。在这种情况下,通知调用的结束实际上并不标志着侦听器执行的结束。如果这是一个真正的问题(即,您知道并关心其他用户定义的、精心设计的侦听器会执行此类操作,那么您可能需要细化并重新审视该问题。
    • @Noel:这是一个很好的观点。尽管侦听器使用不同的线程来执行长时间运行的任务(这是可取的)这一事实意味着它应该将他自己标记为“准备好接受更多”,因此侦听器代码实际上已经准备好了。另一种方法是使用共享标志,并且在所有代码准备好之前不会设置该标志......嗯,让我看看我是否可以编写一些代码来更好地解释这一点。
    • @Noel:您是指 jvm 开始在不同线程中执行侦听器代码的情况,还是当我决定在另一个线程中运行我的一些代码时?如果您指的是第二种情况,那不是问题,因为我不会使用其他线程。如果不是,那确实是个问题;-)
    • @czuk:是后者,当您实际在新线程中执行侦听器工作时。对于这种情况,我添加了“其他”答案。如果您不在这种情况下,请忽略该答案。 :)
    【解决方案2】:

    至于诺埃尔评论:

    如果一个特定的侦听器实现提前返回一个通知,并在单独的线程上“带外”执行工作,这种技术可能会遇到问题。在这种情况下,通知调用的结束实际上并不标志着侦听器执行的结束。如果这是一个实际的问题(即,您知道并关心其他用户定义的、精心制作的侦听器会执行此类操作,那么您可能需要细化并重新审视该问题。

    使用SwingUtilities.invokeLater(),您可以确定在通知所有侦听器之后执行代码。但是其中一个侦听器可能会在一个单独的线程中执行它的工作。

    如果您需要在所有侦听器不仅被通知而且他们完成工作之后执行您的代码,您可以执行以下操作:

    伪代码:

    Listener implements MouseListener 
        +mouseClicked( event: MouseEvent ) 
            SwingUtilities.invokeLater( // returns immediately 
                  someTask() 
            )
    
        -someTask()
            // perform some long task.
    

    如果你的听众是

     addListener( new Listener() )
     addListener( new Listener() )
     addListener( new Listener() )
     addListener( new ExecuteAtTheEnd() ) 
    

    如果您的所有(或部分)听众在不同的线程中执行他们的工作。您的ExecuteAtTheEnd 可能会在通知结束时执行其代码,但不会在侦听器代码执行结束时执行。

    那么,你是做什么的?

    您必须同步某个锁定标志的使用并执行您的代码,直到该标志未被使用为止。

    它可能是一个计数器,在监听器执行时递增,不执行时递减:

     Listener implements MouseListener 
        +mouseClicked( event: MouseEvent )
            lockFlagCount++ 
            SwingUtilities.invokeLater( 
                  someTask() 
            )
    
        -someTask()
            // perform some long task.
            lockFlag--
    

    然后你继续直到这个标志为假:

      ExecuteAtTheEndListener implements MouseListener 
    
           + mouseClicked( event: MouseEvent ) 
            SwingUtilities.invokeLater( 
                  executeAtTheEnd() 
            )
           - executeAtTheEnd() 
                while( logFlagCount > 0 ) 
                    wait()
    
                 if( logFlagCount == 0 ) 
                     // continue with the task.                  
    

    当然,我把它放在伪代码中要复杂得多,但它应该可以工作,如果你在这种情况下。

    【讨论】:

    • 感谢您的回复!编写它必须花一些时间;-) 但是,我可以确定 ExecuteAtTheEnd() 将在其他侦听器之前开始执行吗?是否有可能将其作为最后一个通知但在计数器被其他侦听器递增之前执行?
    • 是的。您必须为此使用良好的锁定机制。我知道如何使用线程上使用的waitnotifyAll 机制来做到这一点,但是写下来有点长,这就是我使用伪代码的原因。我认为如果您处于这种情况,您可以查看 java.util.concurrent API,我在这种情况下更容易听到它(并且您总是可以在 SO 上创建一个新问题来了解它)但是在此之前你必须确定你需要它。从你原来的问题来看,你不需要那个,我原来的答案应该足够了。
    • 我将使用这两种解决方案的组合,因为一方面侦听器列表可能会随时间变化,另一方面某些计算可能会很长。
    【解决方案3】:

    我相信您会想要使用 SwingUtilities.invokeLater() 方法,该方法将在处理完所有其他 GUI 事件后在 Event Dispatch Thread 上调用 Runnable 实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多