简体   繁体   中英

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

This question is about the usage of the volatile keyword. I have a swing application which shows a table and there is a separate thread that adds rows to the table model. 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. 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. And also, the question linked above mentions the synchronized keyword as well. I don't understand how the problem of a thread not seeing the latest value can be fixed using the synchronized keyword. Any explanation on that is also appreciated.

Wrap the call dm.addElement(count++); in SwingUtilities.invokeAndWait() instead

From 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.

Swing was designed to have all updates done on the Event Dispatch Thread. 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. Read the section from the Swing tutorial on Concurrency for more information.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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