在Java中使用Swing,我怎么知道用invokeLater启动的所有线程何时完成?

时间:2014-04-30 12:33:58

标签: java multithreading swing invokelater

在javax.swing.SwingUtilities.invokeLater调用的createAndShowGUI()方法中,如下所示......:

public static void main(String[] args) { 
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() { 
            createAndShowGUI(); 
        } 
    }); 
}

...我有以下代码,它使用invokeLater启动多个线程,其中每个线程在运行时增加progBar的值:

int times = 20;

for(int x = 0; x < times; x = x+1) {
        new Thread("T") {
                public void run() {

                        try {                                              
                            Thread.sleep(5000);

                        } catch(InterruptedException ex) {
                            Thread.currentThread().interrupt();
                        }

                        SwingUtilities.invokeLater(new Runnable() {
                                public void run() {
                                        progBar.setValue(progBar.getValue()+1);
                                }
                        });

                }

        }.start();

}

我如何知道所有线程的完成位置?如果我在invokeLater中使用计数器,我想我会遇到竞争条件。那么正确的方法是什么?我应该使用互斥锁吗? Swing是否为此提供了一些设施?感谢。

我已根据http://www.java2s.com/Code/Java/Threads/InvokeExampleSwingandthread.htm

以这种方式实现了代码

3 个答案:

答案 0 :(得分:2)

传递给Runnable的{​​{1}}都在单个事件调度线程上执行。由于它们都是由不同的线程发送的,因此没有特定的顺序,但它们是一个接一个地执行的。

因此,一旦您通过将UI更新移动到传递给invokeLater的{​​{1}}来修复代码,您就可以使用Swings通知机制来了解作业的完成情况:

Runnable

答案 1 :(得分:1)

InvokeLater实际上运行了从Swing EventDispatchThread调用的所有东西 - 这可能根本不是你想要的。它们不会同时运行,它们会一个接一个地运行。更糟糕的是,在您的示例中,您正在从线程中更改Swing控件,但之后使用InvokeLater

这意味着计数器实际上是线程安全的。

您可能需要使用'SwingWorker'或'ExecutorService'并将结果发回EDT进行显示。

答案 2 :(得分:-2)

  

我如何知道所有线程的完成位置?如果我使用计数器   在invokeLater内部我想我会遇到竞争条件..

  • 不可能,因为这是没有break;
  • 的无限循环
  

那么正确的做法是什么?

  • JProgressBar在API中的范围从0到100,只是为了测试循环中的值是100还是更大,然后使用break;

  • 使用Runnable #Thread而不是普通的vanilla线程

例如(相同的输出应该来自SwingWorker,但是有超过12个实例,AFAIK注意有一个关于重载数量的错误...,但是为了目的,因为论坛的代码可以创建similair代码使用SwingWorker)

enter image description here

enter image description here

enter image description here

import java.awt.Component;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class TableWithProgressBars {

    private static final int maximum = 100;
    private JFrame frame = new JFrame("Progressing");
    Integer[] oneRow = {0, 0, 0, 0};
    private String[] headers = {"One", "Two", "Three", "Four"};
    private Integer[][] data = {oneRow, oneRow, oneRow, oneRow, oneRow,};
    private DefaultTableModel model = new DefaultTableModel(data, headers);
    private JTable table = new JTable(model);

    public TableWithProgressBars() {
        table.setDefaultRenderer(Object.class, new ProgressRenderer(0, maximum));
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Object waiter = new Object();
                synchronized (waiter) {
                    int rows = model.getRowCount();
                    int columns = model.getColumnCount();
                    Random random = new Random(System.currentTimeMillis());
                    boolean done = false;
                    while (!done) {
                        int row = random.nextInt(rows);
                        int column = random.nextInt(columns);
                        Integer value = (Integer) model.getValueAt(row, column);
                        value++;
                        if (value <= maximum) {
                            model.setValueAt(value, row, column); 
                            // model.setValueAt(... must be wrapped into invokeLater()
                            // for production code, otherwise nothing will be repainted
                            // interesting bug or feature in Java7 051, looks like as 
                            // returns some EDT features from Java6 back to Java7 ???
                            try {
                                waiter.wait(15);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        done = true;
                        for (row = 0; row < rows; row++) {
                            for (column = 0; column < columns; column++) {
                                if (!model.getValueAt(row, column).equals(maximum)) {
                                    done = false;
                                    break;
                                }
                            }
                            if (!done) {
                                break;
                            }
                        }
                    }
                    frame.setTitle("All work done");
                }
            }
        }).start();
    }

    private class ProgressRenderer extends JProgressBar implements TableCellRenderer {

        private static final long serialVersionUID = 1L;

        public ProgressRenderer(int min, int max) {
            super(min, max);
            this.setStringPainted(true);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
                boolean isSelected, boolean hasFocus, int row, int column) {
            this.setValue((Integer) value);
            return this;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TableWithProgressBars();
            }
        });    
    }
}