JTableModelListener.tableChanged()线程安全吗?

时间:2014-08-27 05:12:27

标签: java multithreading swing jtable tablemodel

tableChanged()线程的JTable调用是否安全,以便允许我从另一个线程调用它,例如完成下载的内容?我想tableChanged()只是将一个新事件放入Event队列,以便Event-Dispatcher-Thread将来某个时候更新JTable,但这个添加线程是否安全?

1 个答案:

答案 0 :(得分:3)

简短回答否,它不是线程安全的,所有对tableChanged的调用都应该在事件调度线程的上下文中进行。

如果您需要更新TableModel,请将其与表格断开连接并在EDT范围内的一步(setModel)中应用它,或者将更新同步回模型通过使用SwingWorkerEventQueue.invokeLater

进行EDT

使用Swing的一般经验法则,假设没有什么是线程安全的并且保护它。

  

我想tableChanged()只是将一个新事件放入Event队列,以便Event-Dispatcher-Thread将在未来的某个时刻更新JTable,但这个添加线程是否安全?

并非所有事件都在事件队列中进行了调度,许多事件只是由组件中的for-next循环处理,直接循环注册的侦听器,就像TableModel' s fire事件方法......

例如,来自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);
        }
    }
}

这意味着fireTableChanged方法将在调用它的线程的上下文中执行,并且将在同一个线程内通知其侦听器。

这意味着,如果您要从其他主题调用TableModel.setValueAt,则会调用fireTableCellUpdated,这会调用fireTableChanged并最终在上下文中调用tableChanged那个帖子......

作为旁注,你不应该直接打电话给JTable#tableChanged,它是公开的副作用(创建JTable时内部课程没有;)) ,您应该对表格模型进行修改,并允许模型触发事件通知。

<强>更新...

考虑这个非常基本的测试......

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);

    }

}

将输出...

 isEventDispatchingThread - false

因为在EDT中没有发生更新,事实上,它根本没有被事件队列调度......

更新了EDT的实例化和单独的更新线程

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();

            }
        });
    }

}

...输出

 isEventDispatchingThread - false