0

在 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以这种方式实现了代码

4

3 回答 3

2

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

因此,一旦您通过将 UI 更新移动到Runnable您传递给invokeLater您的代码来修复您的代码,您就可以使用 Swings 通知机制来获取有关作业完成的通知:

final int end=progBar.getValue() + times;
progBar.addChangeListener(new ChangeListener() {
  public void stateChanged(ChangeEvent e) {
    if(progBar.getValue()==end) {
      System.out.println("all jobs finished");
    }
  }
});
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();
于 2014-04-30T13:10:10.533 回答
1

InvokeLater实际上运行从 Swing 调用的所有内容EventDispatchThread- 这可能根本不是您想要的。它们不会同时运行,它们将一个接一个地运行。更糟糕的是,在您的示例中,您正在从线程中对 Swing 控件进行更改,然后再使用InvokeLater

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

您在这里可能想要的是使用“SwingWorker”或“ExecutorService”并将结果发布回 EDT 以供显示。

于 2014-04-30T12:39:35.180 回答
-2

我怎么知道所有线程都在哪里完成?如果我在 invokeLater 中使用计数器,我想我会遇到竞争条件..

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

那么正确的方法是什么?

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

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

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

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

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();
            }
        });    
    }
}
于 2014-04-30T13:09:49.047 回答