为什么doAction()会尽早完成()?

时间:2015-04-14 17:37:42

标签: java swing concurrency swingworker

我有点奇怪的问题。我有一个带有doAction()循环的for Swingworker,可以从JTable和数据库中删除行。如果for循环很大,done()方法将在循环完成之前触发,有效地中途执行。

我的代码:

 public void doAction() throws Exception {
    int selectedRows[];
    int modelRows[];
    java.util.ArrayList<customRecord> condemned;
    customTableModel dataSource;
    boolean databaseDelete = false;
    Object uniqueID;
    int rowCount = 0;

    dataSource = (customTableModel)table.getModel();
    selectedRows = table.getSelectedRows();

    condemned = new java.util.ArrayList<customRecord>();
    modelRows = new int[selectedRows.length];

    for(int i=0;i<selectedRows.length;i++)
        modelRows[i] = table.convertRowIndexToModel(selectedRows[i]);

    selectedRows = null;

    for(int i= 0; i < modelRows.length; i++){
        uniqueID = dataSource.getUniqueID(modelRows[i]);

        System.out.println("Debug: spot 1, rowCount = " + rowCount + ", i = " + i + ", length = " + modelRows.length);

        if(uniqueID == null)
            dataSource.removeRow(modelRows[i]);
        else if(adminPrivileges){
            condemned.add(//Record being looked at);
            databaseDelete = true;

            System.out.println("Debug: spot 2, rowCount = " + rowCount + ", i = " + i);

            dataSource.removeRow(modelRows[i]);
            if(condemned.size() >= 100){
                database.clearData(condemned);
                condemned.clear();
            }

            System.out.println("Debug: spot 3, rowCount = " + rowCount + ", i = " + i);

        }

        System.out.println("Debug: spot 4, rowCount = " + rowCount + ", i = " + i + ", uniqueID = " + uniqueID.toString() + "\n");

        if(++rowCount >= 1000){
            System.out.println("rowCount = " + rowCount + ", in break");
            break;
        }
    }

    if(databaseDelete){
        if(condemned.size() > 0)
            database.clearData(condemned);
        loadData(table,database,filterParams);
    }
}

public void done() {

    System.out.println("Debug: in done()");

    table.setVisible(true);
    //Display warning message if the number of rows reached the limit
    if(rowCount >= 1000){
        // Display Limit Reached Warning;
    }
}

我的输出如下:

Debug: spot 1, rowCount = 0, i = 0, length 1006
Debug: spot 2, rowCount = 0, i = 0
Debug: spot 3, rowCount = 0, i = 0
Debug: spot 4, rowCount = 0, i = 0, uniqueID = 2608
.
.
.
Debug: spot 1, rowCount = 505, i = 505, length = 1006
Debug: spot 2, rowCount = 505, i = 505
Debug: spot 3, rowCount = 505, i = 505
Debug: spot 4, rowCount = 505, i = 505, uniqueID = 3073

Debug: spot 1, rowCount = 506, i = 506, length = 1006
Debug: in done()

如果将大for循环设置为像for(i = modelRows.length-1; i >= 0; i--)那样从大到小,那么它会更进一步:

Debug: spot 1, rowCount = 0, i = 1005, length 1006
Debug: spot 2, rowCount = 0, i = 1005
Debug: spot 3, rowCount = 0, i = 1005
Debug: spot 4, rowCount = 0, i = 1005, uniqueID = 3073
.
.
.
Debug: spot 1, rowCount = 899, i = 106, length = 1006
Debug: spot 2, rowCount = 899, i = 106
Debug: spot 3, rowCount = 899, i = 106
Debug: spot 4, rowCount = 899, i = 106, uniqueID = 2174

Debug: in done()

如何正确完成/允许此doAction()方法?是否有一个Swingworker在调用done()方法之前执行的最长时间?

修改

我相信自己(也可能是其他所有人)都说doAction()SwingWorkerdoAction()是在doInBackground()扩展的类中从SwingWorker调用的方法。

代码:

protected interface LengthyAction{

    public customActions getAction();

    public java.awt.Component getComponent();

    public void doAction() throws Exception;

    public void done();

}

private class DataSwingWorker extends javax.swing.SwingWorker{

    private LengthyAction action;

    public DataSwingWorker(LengthyAction targetAction){
        action = targetAction;
        setCursor(action.getComponent(),java.awt.Cursor.WAIT_CURSOR);
        if(listener != null)
            listener.actionPerformed(new java.awt.event.ActionEvent(**stuff**));
    }
    .
    .
    .
    @Override
    protected Object doInBackground() throws Exception {
        action.doAction();
        return null;            
    }

    @Override
    protected void done() {
        if(listener != null)
            listener.actionPerformed(new java.awt.event.ActionEvent(**stuff**));                
        action.done();
    }

}

我找到了问题的答案,这与我最初的想法无关。取出:

if(condemned.size() >= 100){
    database.clearData(condemned);
    condemned.clear();
}

在大doAction() for循环中,一切正常。似乎这导致uniqueID跳过前100个位置,我在循环结束之前击中了数组的末尾,给出NullPointerException

感谢所有的帮助,它最终让我走上了正确的道路。

1 个答案:

答案 0 :(得分:2)

根据您发布的数据,我会尝试回答迄今为止似乎负责的问题:

  

我有一个带有for循环的doAction()Swingworker来从JTable和数据库中删除行......不,它是从doInBackground()调用的对象上的doAction()方法方法。

这不好。如果您从SwingWorker的doAction()方法中调用doInBackground(),这意味着您的代码通过进行Swing调用,特别是调用Swing组件状态的调用,从而公然违反Swing线程规则,在后台线程中。事实上,这可能是导致运行此后台线程的间歇性问题的原因或至少是主要原因。

  

在调用done()方法之前,Swingworker是否会执行最长时间?

没有。当doInBackground()方法完成其操作并且未找到任何异常时调用它。一个关键问题是:您是否在检查SwingWorker运行期间抛出的异常?这将通过在try / catch块中的SwingWorker上调用get()并在完成其操作后进行测试。

  

如何正确完成/允许此doAction()方法完成?

不幸的是,根据您当前的代码很难判断,因为我们无法编译或运行它。如果仍然卡住,请创建并发布您的minimal example programSSCCE

我的主要建议:

  1. 首先,重新编写所有这些代码,使其严格遵循Swing线程规则。这是解决问题的最大机会。了解SwingWorkers自动允许PropertyChangeListener支持,因此这可能是后台线程和Swing事件线程之间进行通信的好方法。
  2. 接下来一定要重构代码,以便你的类是可以独立测试的小单元,然后通过尝试以任何你能想到的方式使它们失败来测试它们中的每一个。