JTableModelListener.tableChanged() thread safe?

僤鯓⒐⒋嵵緔 提交于 2019-12-11 07:57:42

问题


Is the tableChanged() call on a JTable thread safe, so that i am allowed to call it from another Thread, which for example finished downloading something? I imagine tableChanged() to just put a new Event into the Event queue, so that the Event-Dispatcher-Thread will update the JTable at some point in the future, but is this adding thread safe?


回答1:


Short answer no, it is not thread safe and all calls to tableChanged should be made from within the context of the Event Dispatching Thread.

If you need to update a TableModel, either disconnect it from the table and apply it in a single step (setModel) within the confines of the EDT or sync the updates to the model back to the EDT through the use of a SwingWorker or EventQueue.invokeLater

General rule of thumb with Swing, assume nothing is thread safe and guard for it.

I imagine tableChanged() to just put a new Event into the Event queue, so that the Event-Dispatcher-Thread will update the JTable at some point in the future, but is this adding thread safe?

Not all events get scheduled on the Event Queue, many are simply processed by a for-next loop within the component, looping through the registered listeners directly, as is the case for TableModel's fire event methods...

For example, from the AbstractTableModel...

public void fireTableChanged(TableModelEvent e) {
    // Guaranteed to return a non-null array
    Object[] listeners = listenerList.getListenerList();
    // Process the listeners last to first, notifying
    // those that are interested in this event
    for (int i = listeners.length-2; i>=0; i-=2) {
        if (listeners[i]==TableModelListener.class) {
            ((TableModelListener)listeners[i+1]).tableChanged(e);
        }
    }
}

This means that the fireTableChanged method will be executed within the context of the thread that called it, and will notifiy its listeners from within the same thread.

This means that if you were to call TableModel.setValueAt from a different thread, it would call fireTableCellUpdated, which would call fireTableChanged and would eventually call tableChanged within the context of that thread...

As a side note, you should not be calling JTable#tableChanged directly, it's public as a side effect (inner classes weren't around when JTable was created ;)), you should be making modifications to the table's model and allowing the model to trigger the event notifications.

Updated...

Consider this very basic test...

public class Test {

    public static void main(String[] args) {

        DefaultTableModel model = new DefaultTableModel(new String[]{"One"}, 1);
        model.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                System.out.println("isEventDispatchingThread - " + EventQueue.isDispatchThread());
            }
        });

        model.setValueAt("Test", 0, 0);

    }

}

Which will output...

 isEventDispatchingThread - false

Because the update did not occur within the EDT, in fact, it was not dispatched by the Event Queue at all...

Updated with instantiation of EDT and separate update thread

import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

public class Test {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JLabel("Boo"));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                DefaultTableModel model = new DefaultTableModel(new String[]{"One"}, 1);
                model.addTableModelListener(new TableModelListener() {
                    @Override
                    public void tableChanged(TableModelEvent e) {
                        System.out.println("isEventDispatchingThread - " + EventQueue.isDispatchThread());
                    }
                });

                Thread t = new Thread(new Runnable() {

                    @Override
                    public void run() {
                        model.setValueAt("Test", 0, 0);
                    }
                });
                t.start();

            }
        });
    }

}

Outputs...

 isEventDispatchingThread - false


来源:https://stackoverflow.com/questions/25519336/jtablemodellistener-tablechanged-thread-safe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!