JTableModelListener.tableChanged()线程安全吗? [英] JTableModelListener.tableChanged() thread safe?

查看:76
本文介绍了JTableModelListener.tableChanged()线程安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

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

如果您需要更新TableModel,则可以将其从表中断开并在EDT范围内一步(setModel)进行应用,或者将更新更新到模型并通过EDT将其同步回EDT.使用SwingWorkerEventQueue.invokeLater

Swing的一般经验法则,假设没有什么是线程安全的,并为此提供保护.

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

并非所有事件都在事件队列中安排,许多事件仅由组件内的for-next循环处理,直接循环通过已注册的侦听器,就像TableModelfire事件方法一样. ..

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

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?

解决方案

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

这篇关于JTableModelListener.tableChanged()线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆