从基于Swing的应用程序中的线程(EDT除外)接收更新

时间:2015-03-06 05:46:08

标签: java multithreading swing synchronized volatile

这个问题是关于volatile关键字的用法。我有一个swing应用程序,它显示了一个表,并且有一个单独的线程可以向表模型添加行。根据{{​​3}},似乎我必须将某些字段标记为volatile,以确保EDT看到对表模型进行的更新。但即使没有使列表易变,更新似乎也有效。以下是我的代码

import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        JFrame frame = new JFrame("FrameDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        MyTableModel dm = new MyTableModel();
        frame.getContentPane().add(new JScrollPane(new JTable(dm)), BorderLayout.CENTER);

        new Thread(new Runnable() {
            int count = 0;

            @Override
            public void run() {
                while (true) {
                    dm.addElement(count++);
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        frame.pack();
        frame.setVisible(true);
    }

    class Row {
        private String name;
        private String value;

        public Row(String name, String value) {
            super();
            this.name = name;
            this.value = value;
        }

    }

    class MyTableModel extends AbstractTableModel {
        private List<Row> rows = new ArrayList<Test.Row>();

        @Override
        public int getRowCount() {
            return rows.size();
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            if (columnIndex == 0) {
                return rows.get(rowIndex).name;
            } else if (columnIndex == 1) {
                return rows.get(rowIndex).value;
            } else {
                throw new IllegalArgumentException();
            }
        }

        private void addElement(int i) {
            rows.add(new Row("Name" + i, "Vlaue0" + i));
            fireTableRowsInserted(rows.size() - 1, rows.size() - 1);
        }

        @Override
        public String getColumnName(int column) {
            return column == 0 ? "Name" : "Value";
        }
    }

}

在标记rows变量volatile之前,我想知道为什么我必须这样做,如果我已经工作了。此外,上面提到的问题也提到了synchronized关键字。我不明白如何使用synchronized关键字修复线程无法看到最新值的问题。对此的任何解释也表示赞赏。

2 个答案:

答案 0 :(得分:1)

在SwingUtilities.invokeAndWait()中包含呼叫dm.addElement(count++);而不是

来自javadoc

 * Causes <code>doRun.run()</code> to be executed synchronously on the
 * AWT event dispatching thread.  This call blocks until
 * all pending AWT events have been processed and (then)
 * <code>doRun.run()</code> returns. This method should
 * be used when an application thread needs to update the GUI.
 * It shouldn't be called from the event dispatching thread.
 * Here's an example that creates a new application thread
 * that uses <code>invokeAndWait</code> to print a string from the event
 * dispatching thread and then, when that's finished, print
 * a string from the application thread.
 * <pre>
 * final Runnable doHelloWorld = new Runnable() {
 *     public void run() {
 *         System.out.println("Hello World on " + Thread.currentThread());
 *     }
 * };
 *
 * Thread appThread = new Thread() {
 *     public void run() {
 *         try {
 *             SwingUtilities.invokeAndWait(doHelloWorld);
 *         }
 *         catch (Exception e) {
 *             e.printStackTrace();
 *         }
 *         System.out.println("Finished on " + Thread.currentThread());
 *     }
 * };
 * appThread.start();
 * </pre>

答案 1 :(得分:0)

  

但即使没有使列表变得不稳定,更新似乎也有效。

是的,它可能会在99.9%的时间内继续工作。

Swing旨在在Event Dispatch Thread上完成所有更新。这是为了防止从不同的线程完成更新。

在您的情况下,您仍然只在一个线程上完成更新,因此您不太可能遇到问题。但是,如果您确实遇到问题,则无法重现该问题,因为它是随机的,您不希望最大浪费时间调试随机但是。

因此,请确保所有更新都在EDT上完成。有关详细信息,请阅读Concurrency上的Swing教程中的部分。