有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java JOptionPane如何不尊重同步?

我有一个系统,我需要能够问一个简单的“继续?”风格问题。有一个文本字段和一个按钮。要么单独修改文本字段,要么单独按下按钮,我需要能够要求用户确认。因此,我在文本字段上有一个焦点侦听器,在按钮上有一个动作侦听器

但是,如果我编辑该字段,并立即单击该按钮,将弹出两个确认对话框——一个来自焦点侦听器,另一个来自动作侦听器。现在,我显然只需要确认一次,所以我尝试同步抛出JOptionPane的函数,这样就不会输入两次,但似乎没有效果

一些代码演示了这一点:

public class Main extends JFrame {
    static public void main(String [] args) {
        SwingUtilities.invokeLater(() -> {
            Main m = new Main();
            m.setVisible(true);
        });
    }

    private JTextField field;
    private JButton button;

    private boolean isValid;

    public Main() {
        isValid = false;

        setLayout(new BorderLayout());

        field = new JTextField(15);
        field.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent arg0) {}

            @Override
            public void focusLost(FocusEvent e) {
                doValidate();
            }
        });
        add(field, BorderLayout.CENTER);

        button = new JButton("Push Me");
        button.addActionListener((ActionEvent e) -> {
            doValidate();
            doAction();
        });
        add(button, BorderLayout.SOUTH);

        pack();
    }

    private synchronized void doValidate() {
        System.out.println("Validating");
        if(!isValid) {
            int answer = JOptionPane.showConfirmDialog(this, "Really do it?");
            if(answer == JOptionPane.YES_OPTION)
                isValid = true;
        }
    }

    private void doAction() {
        System.out.println("Action done!");
    }
}

首先,这有点令人惊讶。我希望有人能解释一下Swing/JOptionPane是如何绕过synchronized关键字的

至于如何处理,我的想法是需要实现一个线程,等待确认对话框的答案。然后,我需要检查线程是否处于活动状态(如果对话框已经启动,也就是说),如果它已启动,我需要在给出答案时添加一个侦听器。我已经开始这样做了,但这感觉像是在设计问题的解决方案,特别是因为我对为什么会发生这种情况的理解非常有限。此外,我已经可以想象到至少在一些不同的情况下,我最终会遇到麻烦,这可能会使它更容易出错

==编辑===

需要说明的是,这些消息框是同时弹出的,而不是按顺序弹出的。例如:

Two panes


共 (2) 个答案

  1. # 1 楼答案

    Synchronized(同步)意味着两个线程不能同时进入块,永远不能。他们只需等待其他线程释放对该方法的锁定,然后调用它。因此你有两个对话

    这可能与此无关,因为我认为UI呈现是同一个线程,所以您只需从两个单独的UI事件中调用同一个方法两次(这一点我可能错了)

    至于如何让它工作,不要担心线程,而是查看DocumentListener

  2. # 2 楼答案

    我建议添加以下内容,以便单击红色的x将退出程序

    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);   //new
    
    请考虑只需删除按钮侦听器即可得到想要的行为。

    以下是我认为可以让您实现所需行为的代码:

    package swingdemo1;
    
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    
    public class SwingDemo1 extends JFrame {
        static public void main(String [] args) {
            SwingUtilities.invokeLater(() -> {
                SwingDemo1 m = new SwingDemo1();
                m.setVisible(true);
            });
        }
    
        private JTextField field;
        private JButton button;
    
        private boolean isValid;
        private boolean validationDone;
        String state;
    
        public SwingDemo1() {
    
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);   //new
    
            isValid = false;
    
            setLayout(new BorderLayout());
    
            field = new JTextField(15);
            field.addFocusListener(new FocusListener() {
                @Override
                public void focusGained(FocusEvent arg0) {
    
                }
                @Override
                public void focusLost(FocusEvent e) {               
                        doValidate();
                        doAction();                                    
                }
            });
            add(field, BorderLayout.CENTER);
    
            button = new JButton("Push Me");
            /*                                                            //new
            button.addActionListener((ActionEvent e) -> {
                System.out.println("ActionEvent: " + e.getActionCommand());              
                    doValidate();
                    doAction();
                });
            */                                                            //new
            add(button, BorderLayout.SOUTH);
    
            pack();
        }
    
        private synchronized void doValidate() {
            System.out.println("Validating");
            if(!isValid) {
                int answer = JOptionPane.showConfirmDialog(this, "Really do it?");
                if(answer == JOptionPane.YES_OPTION) 
                    isValid = true;
            }        
        }
    
        private void doAction() {
            System.out.println("Action done!");
        }
    }