我正在尝试制作一个简单的gui应用程序,它启动一个可以启动和停止的“长”进程。该过程生成随机数,并在JList中显示它们。当显示数字(即进程启动)时,用户可以从JList和delete中选择一个数字。gui和uml http://mypages.valdosta.edu/dgibson/courses/cs4322/post.jpg
我很想知道我能对我的方法做什么改进。(是的,我需要阅读垃圾邮件在美国东部时间和SwingWorker上的文章,我会的!)
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();
}
}发布于 2014-02-20 06:17:24
view.list.getSelectedIndices()方法中包括deleteElement()。好消息是,这个线程已经运行了actionPerformed事件处理程序(它调用了deleteElement()),所以很好,但是您可以摆脱EventQueue.invokeLater,直接在那里调用view.listModel.remove(index)。deleteElement()方法中,索引/索引操作不太方便: final index =indexindices.length -1-i;需要一段时间才知道它在做什么,这是因为从列表中删除一个元素,会在删除元素之后移动元素的索引,因此您必须以相反的顺序删除它们。我只需反转数组并使用foreach循环:ArrayUtils.reverse(索引);for (final int elementIndex: int){ view.listModel.remove(elementIndex);} ArrayUtils来自阿帕奇公域朗。(它是开源,所以如果不想只包含一个助手方法的库,就可以将该方法复制到代码中。)发布于 2014-02-19 18:07:42
您的代码结构良好,易于阅读。这是件好事。
据我所见,您似乎正在使用适当的线程来执行swing和非swing工作。这很好。
有几个线程安全问题我可以看到:
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);}};}}https://codereview.stackexchange.com/questions/42089
复制相似问题