JavaFX应用程序线程的预期行为是什么

JavaFX应用程序线程的预期行为是什么,java,javafx,javafx-8,java-threads,Java,Javafx,Javafx 8,Java Threads,EventHandler实现X附加到窗格并侦听所有MouseEvents。当然,X有一个handle()方法,它从JavaFX应用程序线程接收MouseEvents 窗格包含一个矩形。当窗格收到MouseEvent.MOUSE_单击矩形时,X执行两项操作: 从窗格中删除矩形,然后立即添加另一个矩形(这可能会导致其他事件) 继续进行一些非常规处理 问题是: 在JavaFX应用程序线程通过handle()向X提交任何其他事件之前,步骤2中的处理是否有望完成?注意,步骤1可能会触发其他事件 只是寻找一

EventHandler实现X附加到窗格并侦听所有MouseEvents。当然,X有一个
handle()
方法,它从JavaFX应用程序线程接收MouseEvents

窗格包含一个矩形。当窗格收到MouseEvent.MOUSE_单击矩形时,X执行两项操作:

  • 从窗格中删除矩形,然后立即添加另一个矩形(这可能会导致其他事件)

  • 继续进行一些非常规处理

  • 问题是:

    在JavaFX应用程序线程通过
    handle()
    向X提交任何其他事件之前,步骤2中的处理是否有望完成?注意,步骤1可能会触发其他事件

    只是寻找一个肯定或否定的答案。你的答案背后的理由也很好

    我应该补充一点,在任何地方都不涉及任何类型的线程,包括“任意处理”


    编辑:

    示例代码
    这里是它唯一的依赖项,EventListener类。我包含了足够多的javadoc,使程序有意义:


    在JavaFX应用程序线程通过handle()向X提交任何进一步的事件之前,步骤2中的处理是否有望完成

    是的。因此JavaFX线程按顺序执行所有操作。例如,如果要在
    句柄()中添加
    线程。sleep
    method,则JavaFX线程在睡眠完成之前不会执行任何操作。它按顺序执行所有处理,我猜这是线程的定义元素。它不会启动并并行处理其他事件。这在动画中非常重要,因为所有这些处理必须在JavaFX线程计算之前进行单击并显示下一帧

    增编:

    考虑这一点-如果光标位于矩形上,则MouseEvent会引发MouseEvent.MOUSE_EXIT事件,因为在JavaFX看来,这就是刚刚发生的事情。该MouseEvent是在JavaFX应用程序线程上生成的,并将由其处理。现在需要考虑的是JavaFX应用程序线程可以执行并将鼠标退出到X的handle()或继续dcoig任意处理。它是做什么的


    首先处理鼠标点击事件。线程处理完所有触发事件后,它将进入屏幕。更新屏幕完成后,它将处理任何新的触发事件,如鼠标退出。例如,假设您创建一个节点,删除鼠标上的节点,然后将其放回MUSE_退出。当您将鼠标移到此节点上时,它将以帧速率闪烁——而不是在更新屏幕之前进入无休止的循环。

    我丢失了以前的登录,但我是此线程的OP。我只是想更新它,以报告此处的问题是真实的、已确认的,并且Oracle已为其提供了错误ID,您可以重新登录请在此引用:


    感谢所有与我接触的人!希望它能帮助其他人。

    >“继续进行一些辅助处理”你是如何安排、运行、分派附加处理的?好问题。我应该补充一点,没有涉及任何类型的其他线程。我将更新我的问题以反映这一点。我编辑了你的问题,试图澄清你在步骤1中显式触发附加事件。一个小代码测试示例我可能会在很大程度上帮助这个问题得到一个有根据的答案。谢谢你Ryan帮我澄清我的想法并对这个问题进行投票。我明天将发布一个SSCCE,我们都可以对此大吃一惊,哈哈。我已经努力重新格式化你的简短示例,以适应堆栈溢出站点的宽度,并将其编辑到你的在没有答案的情况下,问题会得到解决。请更新我的问题,关于当删除矩形时鼠标光标在哪里。谢谢!若泽,是的,但是结果是违反直觉的。考虑一下,矩形的移除会触发鼠标事件。刚才发生的事情是-光标和矩形已经“分离”。现在要考虑的是。JavaFX应用程序线程可以继续执行并将鼠标出口发送到X的句柄(),在这种情况下,它将返回到
    handle()
    ,或者它可以继续执行“任意处理”,然后将新的鼠标退出事件发布到
    handle()
    。它首先做什么?如果你有证据,告诉我们。因为据我所知,Jose是正确的。Ryan我可以明天(12小时后)做这件事。据我所知,我们都认为相同的事情。我可以通过组合打印来显示(我知道,这可能是相对于它们的执行异步出现的)以及我所说的静态计数器的更新。最初的问题发生在成熟的应用程序中,并以ConcurrentModificationException的形式出现在应用程序线程重新输入时触发的列表上。我花了整整3天加上一个周末才从SSCCE的另一端出来。我还想说一件事,然后闭嘴到明天。这种行为导致了比赛状态和海森堡,这意味着会出现一些跑步(抛出异常)有些则不会,这是在SSCCE中!引发错误的控制变量是正在同时修改的列表的长度。如果列表较短,则列表迭代可能会在再次访问列表之前完成。列表越长,引发异常的可能性越大。列表长度为第二,程序行为是不确定的!
    package bareBonesJavaFXBugExample;
    
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Pane;
    import javafx.stage.Stage;
    
    /**
     * An {@link Application} with one {@link Pane} containing one {@link Label}.
     * The {@link Label} has a single {@link javafx.event.EventHandler}, 
     * {@link LabelEventHandler} which processes all {@link MouseEvent}s the {@link Label}
     * receives.
     * 
     * To trigger the bug, run the application, then spend a second mouse over the 
     * little label in the upper left hand corner of the screen. You will see output to 
     * standard I/O. Then, click the label, which will then disppear. Check the I/O for
     * Strings ending in debugCounter is 1. 
     * 
     * What that String means and how it proves that the JavaFX Application Thread has 
     * become reentrant is explained in the javadoc of {@link LabelEventHandler}.
     */
    public class JavaFXAnomalyBareBonesApplication extends Application
    {
        public void start(Stage primaryStage)
        {
          Pane mainPane = new Pane();
          mainPane.setMinHeight(800);
          mainPane.setMinWidth(800);
    
          Label label = new Label(" this is quite a bug !!!!");
    
          LabelEventHandler labelEventHandler = new LabelEventHandler(mainPane, label);
          label.addEventHandler(MouseEvent.ANY, labelEventHandler);
    
          mainPane.getChildren().add(label);
    
          Scene scene = new Scene(mainPane);
          primaryStage.setScene(scene);
          primaryStage.show();
        }
    
        /**
         * The entry point of application.
         *
         * @param args
         *         the input arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
    }
    
    package bareBonesJavaFXBugExample;
    
    import javafx.event.Event;
    import javafx.event.EventHandler;
    import javafx.scene.control.Label;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Pane;
    
    import java.util.Collection;
    import java.util.ConcurrentModificationException;
    
    /**
     * An {@link EventHandler} implementation for {@link MouseEvent}s.
     * This implementation's {@link EventHandler#handle(Event)} shows the
     * relevant debug information to standard output before and after removing
     * the member {@link #label} from the {@link #pane}.
     *
     * <b>discussion</b><br></br>
     * <p>
     * Users should first satisfy themselves that the value of
     * {@link LabelEventHandler#debugCounter} can only be non-zero, in fact 1
     * (one) in the method {@link LabelEventHandler#showDebugInformation(String)}
     * if the method {@link LabelEventHandler#handle(MouseEvent)}  has been
     * re-entered recursively, that is, before a previous invocation of
     * {@link LabelEventHandler#handle(MouseEvent)} has returned.
     * <p>
     * Proof:
     * 1) <code>debugCounter</code> starts at value 0 (zero).
     * 2) <code>debugCounter</code> is only incremented once, by 1 (one), and that
     *    is after the first call to {@link LabelEventHandler#showDebugInformation(String)}
     *    has returned.
     * 3) <code>debugCounter</code> is only decremented once, by 1 (one) and that
     *    is before the last call to {@link LabelEventHandler#showDebugInformation(String)}.
     * 4) however, because <code>debugCounter</code> is a class variable
     *    (it's static), if handle() is recurvsively re-entered then it's
     *    value can be 1 (one) when the re-entrant
     *
     * Thread executes {@link LabelEventHandler#showDebugInformation(String)}
     *
     * End proof.
     *
     * The output of this method to standard I/O is volumnious but searching the
     * output for the exact String "debugCounter is 1" will immediately show the
     * {@link LabelEventHandler#handle(MouseEvent)} method to have been recursively
     * entered.
     *
     * Some other possibilities other than the JavaFX Application Thread recursing
     * into {@code handle()} need to be addressed.
     * One is the fact that the compiler is free to reorder statements if it can
     * prove that such a reordering would have no effect on the program's correctness.
     *
     * So somehow the compiler is reordering the increment/decrement of
     * {@code  debugCounter} and the calls to {@code   showDebugInformation}.
     * But this would alter the correctness of the program, so this cannot be the case,
     * or the compiler is making an error.
     *
     * Another is the fact that I/O is not instantaneous and can appear to standard
     * output later than it actually was executed.
     * This is something often seen in debug stack traces, where the output is
     * broken up  or interleaved by the output of the stack trace even though the
     * two sets of statments, i/o and stack trace i/o, were strictly ordered in execution.
     * But this can't account for the value of {@code   debugCounter}, so it can't
     * be the reason "debugCounter is 1" appears in output.
     *
     * In fact we can make this recursive behaviour more obviously consequential
     * to the correctness of the program. If {@code   handle() } is being
     * recursively re-entered, then we can force a
     * {@link ConcurrentModificationException} on a {@link Collection}.
     * If we try to invoke {@link Collection#add(Object)} to a {@link Collection}
     * while it is being iterated through, then a {@link ConcurrentModificationException}
     * will be thrown.
     *
     * If we re-write this program slightly to first add or remove to or from a
     * {@link Collection} then iterate through that {@link Collection} within the
     * scope of  execution of {@code   handle()}, <em>and</em> {@code   handle()}
     * is being recursively invoked, then we may see a {@link ConcurrentModificationException}.
     *
     * Two other instances of this same basic program exist at the link provided.
     * They are named {@link JavaFXAnomalySimpleVersionApplication} and
     * {@link JavaFXAnomalyComplexVersionApplication} which is written to throw a
     * {@link ConcurrentModificationException} when the JavaFX Application Thread
     * becomes reentrant.
     *
     * I also have a screen grab (not included here) of the stack trace at a
     * specific moment <code>handle()/code> is being invoked, and it can clearly
     * be seen that the previous executing line was within the scope of execution
     * of the previous invocation of <code>handle()</code>.
     *
     * In the .zip file at the link there is a readme.txt. In that file.
     * I present the two lines of code which need to be added, and where
     * they need to be added,  so as to generate the same stack trace
     * showing the same thing.
     */
    public class LabelEventHandler implements EventHandler<MouseEvent> {
        /**
         * a counter which acts as a recursion detector.
         * If {@link #handle(MouseEvent)} is never recursively invoked by
         * the JavaFX Application Thread, then it's value will never be other
         * than 0 (zero) in {@link #showDebugInformation(String)}.
         */
        private static int debugCounter;
    
        /**
         * The {@link Label} which will disappear when clicked. This causes
         * a MOUSE_EXITED_TARGET event top be fired and that in turn causes
         * the JavaFX Event Dispatch Thread to recurse into this class's
         * {@link #handle(MouseEvent)}
         */
        private Label label;
        /**
         * The {@link Pane} which contains the {@link Label}. The
         * {@link Label} is removed from this {@link Pane}.
         */
        private final Pane pane;
    
        /**
         * Assign the values to the members {@link Pane} and {@link Label}
         */
        public LabelEventHandler(Pane pane, Label label) {
    
            this.pane = pane;
            this.label = label;
        }
    
        /**
         * Causes the member {@link #label} to be removed as a child of the
         * member {@link #pane}.
         *
         * @param mouseEvent the {@link MouseEvent} received from the
         * JavaFX Application Thread from the {@link Label} which this
         * {@link EventHandler} is listening to.
         */
        @Override
        public void handle(MouseEvent mouseEvent) {
    
            // debug can only every be 0 (zero) at this point
            showDebugInformation("ENTERING");
            debugCounter++;
    
    
            if (mouseEvent.getEventType().equals(MouseEvent.MOUSE_PRESSED)
                    && mouseEvent.isPrimaryButtonDown()) {
                pane.getChildren().remove(label);
            }
    
            debugCounter--;
            // debug can only every be 0 (zero) at this point
            showDebugInformation("EXITING");
        }
    
        /**
         * Displays two values to standard output. The first is a
         * {@link String}  indicating whether the
         * {@link LabelEventHandler#handle(MouseEvent)} method is
         * being entered or exited and the second is the value of
         * {@link LabelEventHandler#debugCounter} at the time this
         * method is executed.
         *
         * @param enterOrExit the string ENTERING or EXITING
         * reflecting the point  at which this method was invoked
         * by {@link LabelEventHandler#handle(MouseEvent)}.
         */
        private void showDebugInformation(String enterOrExit) {
    
            System.out.println();
            System.out.print(enterOrExit + " method handle");
            System.out.print(" and debugCounter is " + debugCounter);
            System.out.println();
        }
    }