1

更新:所以我发现是什么导致了这个问题。当我的 Java 窗口最小化时,我的表正在渲染每一行。有谁知道如何防止这种情况?

我有一个 JXTable,它每秒都在不断更新、删除和添加行数据。我们说的是平均每秒修改 10-20 行。

通常,CPU 使用率介于 5% 和 10% 之间。当我们每秒更新数百次时,它可以达到 15%。

但是,我们注意到,当我们的 Java 窗口最小化时,每当有任何更新通过时,我们的 CPU 使用率每次都会达到 25%。我们设置了一个脚本以每 5 秒添加一行,当该单行通过时,我们看到 CPU 使用率达到 25%。

我能想到的唯一解释是使用SwingUtilities.invokeAndWait()。我正在后台线程中修改行数据,并为各种 fireTableDataChanged() 方法使用 invokeAndWait。

我使用 invokeAndWait 因为我需要按顺序触发我的事件。例如,我删除一些行,调用 fireTableRowsDeleted(),然后添加一些行并调用 fireTableRowsInserted()。

任何想法为什么我的 CPU 使用率只有在表更新和窗口最小化时才达到 25%?

4

3 回答 3

2

在 JXTable 中,每当模型更改时都会调用此方法:

protected void postprocessModelChange(TableModelEvent e) {
    if (forceRevalidate && filteredRowCountChanged) {
        resizeAndRepaint();
    }
    filteredRowCountChanged = false;
    forceRevalidate = false;
}

resizeAndRepaint()调用似乎是在窗口最小化时强制重新绘制每一行。如下覆盖似乎可以解决问题:

@Override
protected void resizeAndRepaint()
{
    JFrame window = (JFrame)  SwingUtilities.getAncestorOfClass(JFrame.class, this);
    if(window != null && window.getState() != JFrame.ICONIFIED)
    {
        super.resizeAndRepaint();
    }
}
于 2015-11-23T16:09:48.710 回答
2

我使用 invokeAndWait 因为我需要按顺序触发我的事件

无需使用 invokeAndWait()。你可以只使用invokeLater()。事件仍将按接收顺序执行。

各种 fireTableDataChanged() 方法。

您不应该调用 fireTableDataChanged。TableModel 将调用适当的 fireXXX() 方法。当您可能只更改几行时,为什么要重新绘制整个表格。如有必要,RepaintManager 会将多个绘制请求合并为一个。

编辑:

这是我躺在身边的一些代码。所有更新都在 EDT 上对模型进行,并且代码不会调用 fireXXX(...) 方法:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class TableThread extends JFrame
    implements ActionListener, Runnable
{
    JTable table;
    DefaultTableModel model;
    int count;

    public TableThread()
    {
        String[] columnNames = {"Date", "String", "Integer", "Decimal", "Boolean"};
        Object[][] data =
        {
            {new Date(), "A", new Integer(1), new Double(5.1), new Boolean(true)},
            {new Date(), "B", new Integer(2), new Double(6.2), new Boolean(false)},
            {new Date(), "C", new Integer(3), new Double(7.3), new Boolean(true)},
            {new Date(), "D", new Integer(4), new Double(8.4), new Boolean(false)}
        };

        model = new DefaultTableModel(data, columnNames);
        table = new JTable( model );
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        table.setIgnoreRepaint(false);

        JScrollPane scrollPane = new JScrollPane( table );
        getContentPane().add( scrollPane );

        JButton button = new JButton( "Start Thread to Update Table" );
        button.addActionListener( this );
        getContentPane().add(button, BorderLayout.SOUTH );
    }

    public void actionPerformed(ActionEvent e)
    {
        new Thread( this ).start();
        table.requestFocus();
    }

    public void run()
    {
        Random random = new Random();

        while (true)
        {
            final int row = random.nextInt( table.getRowCount() );

            SwingUtilities.invokeLater(new Runnable()
            {
                public void run()
                {
                    table.setValueAt( new Integer(count++), row, 2);
                    table.setRowSelectionInterval(row, row);
                    Object[] aRow = { new Date(), "f", row, new Double(123), new Boolean(true) };
                    model.addRow( aRow );
                }
            });

            try { Thread.sleep(500); }
            catch(Exception e) {}
        }
    }

    public static void main(String[] args)
    {
        TableThread frame = new TableThread();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setVisible(true);
    }
}

无论框架是可见还是最小化,CPU 都是一致的。

如果您需要更多帮助,请发布正确的SSCCE(如上面的代码)来演示问题。

于 2015-11-21T00:50:55.087 回答
2

JTable使用享元模式仅渲染可见单元格,但主机操作系统可能会通过干扰,例如通过尝试为最小化图标设置动画;配置文件是肯定的。不管是什么原因,publish()更新都注定要TableModeldoInBackground()执行中执行SwingWorker,如下所示。工作人员将合并更新并以process()可持续的速率(约 30 赫兹)交付它们。然后,您TableModel可以以对您的应用程序有意义的方式推迟触发更新事件。

于 2015-11-20T23:11:57.000 回答