首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >长螺纹与EDT

长螺纹与EDT
EN

Code Review用户
提问于 2014-02-19 00:57:11
回答 2查看 163关注 0票数 4

我正在尝试制作一个简单的gui应用程序,它启动一个可以启动和停止的“长”进程。该过程生成随机数,并在JList中显示它们。当显示数字(即进程启动)时,用户可以从JList和delete中选择一个数字。gui和uml http://mypages.valdosta.edu/dgibson/courses/cs4322/post.jpg

我很想知道我能对我的方法做什么改进。(是的,我需要阅读垃圾邮件在美国东部时间和SwingWorker上的文章,我会的!)

代码语言:javascript
复制
public class MVC {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                create();
            }
        });
    }

    private static void create() {
        RandomNumberModel model = new RandomNumberModel();
        Controller controller = new Controller(model);
        View view = new View(controller);
        controller.setView(view);

        JFrame f = new JFrame("EDT & Long Thread");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(view);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

public class View extends JPanel {
    JList list;
    DefaultListModel<Double> listModel;
    JTextArea area;
    JTextField field;
    JButton btnGenerate = new JButton("Start Generation");
    JButton btnStop = new JButton("Stop Generation");
    JButton btnDelete = new JButton("Delete selected");
    Controller controller;

    public View(Controller controller) {
        this.controller = controller;
        this.setSize(new Dimension(600, 300));

        // Create GUI components
        listModel = new DefaultListModel<Double>();
        list = new JList(listModel);
        area = new JTextArea();
        btnStop.setEnabled(false);

        btnGenerate.addActionListener(controller);
        btnStop.addActionListener(controller);
        btnDelete.addActionListener(controller);

        // Assemble GUI components
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(btnGenerate);
        buttonPanel.add(btnStop);

        JPanel listPanel = new JPanel();
        listPanel.setLayout(new BorderLayout());
        listPanel.add(new JScrollPane(list));
        listPanel.add(btnDelete, BorderLayout.SOUTH);

        this.setLayout(new BorderLayout());
        this.add(listPanel, BorderLayout.WEST);
        this.add(buttonPanel, BorderLayout.SOUTH);
        this.add(new JScrollPane(area));
    }
}

public class Controller implements ActionListener {
    static class ControlException extends Exception {}
    static class StopGenException extends ControlException {}

    View view;
    RandomNumberModel model;
    Thread thread = null;
    volatile boolean isStopGen = false;

    public Controller(RandomNumberModel model) {
        this.model = model;
    }

    public void setView(View view) {
        this.view = view;
    }

    public void actionPerformed(ActionEvent e) {
        try {
            if (view.btnGenerate.getActionCommand().equals(
                    e.getActionCommand())) {
                view.listModel.clear();
                startNumGen();
            } else if (view.btnStop.getActionCommand().equals(
                    e.getActionCommand())) {
                stopNumGen();
            } else if (view.btnDelete.getActionCommand().equals(
                    e.getActionCommand())) {
                deleteElement();
            }
        } 
        catch (ControlException exc) {
            System.out.println(exc);
        }
    }

    public void startNumGen() throws ControlException {

        thread = new Thread(new Runnable() {
            public void run() {
                try {
                    doGeneration();
                }
                catch (ControlException exc) {
                    System.out.println("Stopped " + exc);
                } 
                catch (InterruptedException exc) {
                    System.out.println("Interrupted " + exc);
                } 

                finally {
                    view.btnGenerate.setEnabled(true);
                    view.btnStop.setEnabled(false);
                    isStopGen = false;
                }
            }
        });

        view.btnGenerate.setEnabled(false);
        view.btnStop.setEnabled(true);
        thread.start();
    }

    private void doGeneration() throws StopGenException, InterruptedException {
        for(int i=0; i<10; i++) {
            if( isStopGen) {
                throw new StopGenException();
            }
            final double val = model.getRandomNum();
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    view.listModel.addElement(val);
                }
            });
        }
    }

    public void stopNumGen() {
        System.out.println("Stop generation");
        isStopGen = true;
    }

    public void deleteElement() {
        System.out.println("Delete number");
        int indices[] = view.list.getSelectedIndices();

        for (int i = 0; i < indices.length; i++) {
            final int index = indices[indices.length - 1 - i];
            System.out.print("Index: " + index);

            double val = view.listModel.get(index);
            System.out.println(", Deleted val: " + val);
            EventQueue.invokeLater(new Runnable() {
                    public void run() {
                    view.listModel.remove(index);
                }
            });
        }
    }

}

public class RandomNumberModel {
    public double getRandomNum() throws InterruptedException {
        Thread.sleep(500);
        return Math.random();
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2014-02-20 06:17:24

  1. AFAIK和维基百科:所有用户界面组件都应该只从AWT事件调度线程中创建和访问。在view.list.getSelectedIndices()方法中包括deleteElement()。好消息是,这个线程已经运行了actionPerformed事件处理程序(它调用了deleteElement()),所以很好,但是您可以摆脱EventQueue.invokeLater,直接在那里调用view.listModel.remove(index)
  2. deleteElement()方法中,索引/索引操作不太方便: final index =indexindices.length -1-i;需要一段时间才知道它在做什么,这是因为从列表中删除一个元素,会在删除元素之后移动元素的索引,因此您必须以相反的顺序删除它们。我只需反转数组并使用foreach循环:ArrayUtils.reverse(索引);for (final int elementIndex: int){ view.listModel.remove(elementIndex);} ArrayUtils来自阿帕奇公域朗。(它是开源,所以如果不想只包含一个助手方法的库,就可以将该方法复制到代码中。)
票数 2
EN

Code Review用户

发布于 2014-02-19 18:07:42

您的代码结构良好,易于阅读。这是件好事。

据我所见,您似乎正在使用适当的线程来执行swing和非swing工作。这很好。

有几个线程安全问题我可以看到:

  • 守护线程-在可能的情况下,您可以应该将守护进程线程用于后台任务。。这使得JVM关闭变得更容易,并且对于那些不处于Swing环境(这会“硬”关闭)的情况来说,这是一个很好的实践。Thread.setDaemon(真);thread.start();
  • 在动作听者中有一种可能的种族状况.在概念上,可能有多个动作事件排队等待view.btnGenerate。这些操作可以在禁用按钮之前排队。这将导致同时运行两个生成器线程。我会推荐这样的东西:私有final AtomicBoolean generatorRunning =新AtomicBoolean(false);那么,在您的startNumGen线程中,我将有: if (!generatorRunning.compareAndSet(false,true)) { //已经在运行.返回false;}这将显式地对该方法进行门.只有一个线程可以启动生成器(直到它停止),而一个线程只能启动其中的一个(如果有多个排队事件).
  • 此外,我将用这个volatile isStopGen替换generatorRunning AtomicBoolean。易失性是一个复杂的概念,AtomicBoolean也做同样的事情,但是有更好的语义.您可以将最后块中的易失性isStopGen设置替换为:view.btnGenerate.setEnabled(真);view.btnStop.setEnabled(假);generatorRunning.set(false);}说到这些语义,您应该在睡眠后,在集合之前,而不是在睡眠之前检查generatorRunning.get()。这里有一个风险,就是你停止了生成器,然后它在半秒后仍然生成一个值.您的代码看起来可能是:私有的void doGeneration()抛出StopGenException,InterruptedException { for(int i=0;i<10;i++) { final double val = model.getRandomNum();// check在睡眠后.如果( !generatorRunning.get()) {抛出新的StopGenException();}EventQueue.invokeLater(新的Runnable() ){ public void (){ //潜在的重复检查在一个缓慢的进度线程中.if (generatorRunning.get()) { view.listModel.addElement(val);}};}}
票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/42089

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档