简体   繁体   English

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

[英]Recieving updates from a thread(other than the EDT) in a Swing based application

This question is about the usage of the volatile keyword. 这个问题是关于volatile关键字的用法。 I have a swing application which shows a table and there is a separate thread that adds rows to the table model. 我有一个显示表的swing应用程序,还有一个单独的线程向表模型添加行。 According to this it seems that I have to mark some of the fields as volatile to make sure that the EDT sees the updates done to the table model. 根据这个看来我必须标记一些领域的波动,确保了EDT看到做表模型的更新。 But even without making the list volatile, the updates seem to work. 但是,即使在不使列表不稳定的情况下,更新似乎仍然有效。 Following is my code 以下是我的代码

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";
        }
    }

}

Before marking the rows variable volatile, I would like to know why I have to do it if what I have already works. 在将rows变量标记为volatile之前,我想知道如果已经可以工作的话为什么必须这样做。 And also, the question linked above mentions the synchronized keyword as well. 而且,上面链接的问题也提到了synchronized关键字。 I don't understand how the problem of a thread not seeing the latest value can be fixed using the synchronized keyword. 我不明白如何使用synchronized关键字解决线程看不到最新值的问题。 Any explanation on that is also appreciated. 也可以对此进行任何解释。

Wrap the call dm.addElement(count++); 包装调用dm.addElement(count++); in SwingUtilities.invokeAndWait() instead 在SwingUtilities.invokeAndWait()中代替

From javadoc 从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>

But even without making the list volatile, the updates seem to work. 但是,即使在不使列表不稳定的情况下,更新似乎仍然有效。

Yes, and it will probably continue to work 99.9% of the time. 是的,它可能会继续工作99.9%的时间。

Swing was designed to have all updates done on the Event Dispatch Thread. Swing旨在使所有更新都在事件调度线程上完成。 This is to prevent updates from being done from different threads. 这是为了防止从不同线程进行更新。

In your case you still really only have the updates done on one Thread so it is unlikely you will have a problem. 就您而言,您实际上仍然只在一个线程上完成了更新,因此不太可能出现问题。 However, if you do have a problem you will not be able to reproduce the problem since it will be random and you don't want top waste time debugging a random but. 但是,如果您确实有问题,则将无法重现该问题,因为它是随机的,并且您不希望浪费时间调试随机的而是。

So make sure all updates are done on the EDT. 因此,请确保所有更新都在EDT上完成。 Read the section from the Swing tutorial on Concurrency for more information. 阅读Swing 并发教程中的有关更多信息的部分。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM