对tableChanged()
线程的JTable
调用是否安全,以便允许我从另一个线程调用它,例如完成下载的内容?我想tableChanged()
只是将一个新事件放入Event队列,以便Event-Dispatcher-Thread将来某个时候更新JTable
,但这个添加线程是否安全?
答案 0 :(得分:3)
简短回答否,它不是线程安全的,所有对tableChanged
的调用都应该在事件调度线程的上下文中进行。
如果您需要更新TableModel
,请将其与表格断开连接并在EDT范围内的一步(setModel
)中应用它,或者将更新同步回模型通过使用SwingWorker
或EventQueue.invokeLater
使用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