Не удается получить ArrayIndexOutOfBoundsException из Future‹?› и SwingWorker, если поток запускает Executor

Я играю с многопоточностью для SwingWorker с помощью Executor, и я по ошибке определил неправильные элементы из вектора, похоже, что этот код довольно игнорирует этот элемент в векторе, не существует

мой вопрос -> как/можно каким-то образом поймать это исключение(я)

простой вывод

run:
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
BUILD SUCCESSFUL (total time: 11 seconds)

раскомментировать

//changeTableValues1(); // un-comment for get ArrayIndexOutOfBoundsException

все правильно, я получаю ArrayIndexOutOfBoundsException и вывод

run:
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 2
        at java.util.Vector.get(Vector.java:694)
        at KondorExport.Util.Help.Table.TableWithExecutor.changeTableValues1(TableWithExecutor.java:70)
        at KondorExport.Util.Help.Table.TableWithExecutor.access$100(TableWithExecutor.java:18)
        at KondorExport.Util.Help.Table.TableWithExecutor$2.actionPerformed(TableWithExecutor.java:61)
        at javax.swing.Timer.fireActionPerformed(Timer.java:271)
        at javax.swing.Timer$DoPostEvent.run(Timer.java:201)
        at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
BUILD SUCCESSFUL (total time: 10 seconds)

из кода

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.*;
import javax.swing.table.*;

public class TableWithExecutor extends JFrame {

    private static final long serialVersionUID = 1L;
    private String[] columnNames = {"Narrative", "Description"};
    private Object[][] data = {{"About", "About"}, {"Add", "Add"}};
    private JTable table;
    private Executor executor = Executors.newCachedThreadPool();
    private Timer timerRun;
    private int delay = 3000;
    private Vector<String> fwDeals;
    private Vector<String> fwDeals1;

    public TableWithExecutor() {
        DefaultTableModel model = new DefaultTableModel(data, columnNames);
        table = new JTable(model) {

            private static final long serialVersionUID = 1L;

            @Override
            public Class getColumnClass(int column) {
                return getValueAt(0, column).getClass();
            }
        };
        table.setPreferredScrollableViewportSize(table.getPreferredSize());
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
        prepareStartShedule();
    }

    private void prepareStartShedule() {
        timerRun = new javax.swing.Timer(delay, startCycle());
        timerRun.setRepeats(true);
        timerRun.start();
    }

    private Action startCycle() {
        return new AbstractAction("Start Shedule") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                executor.execute(new TableWithExecutor.MyTask("StartShedule")); //non on EDT
                //changeTableValues1(); // un-comment for get ArrayIndexOutOfBoundsException
            }
        };
    }

    private void changeTableValues1() {
        fwDeals1 = new Vector<String>();
        fwDeals1.add("First"); // ElementAt(0)
        fwDeals1.add("Second");// ElementAt(1)
        checkDealsInDb1(fwDeals1.get(1), fwDeals1.get(2));
    }

    private void checkDealsInDb1(String Str, String Str1) {
        table.getModel().setValueAt(Str, 0, 1);
        table.getModel().setValueAt(Str1, 1, 1);
    }

    private void changeTableValues() {
        fwDeals = new Vector<String>();
        fwDeals.add("First"); // ElementAt(0)
        fwDeals.add("Second");// ElementAt(1)
        checkDealsInDb(fwDeals.get(1), fwDeals.get(2));
    }

    private void checkDealsInDb(String Str, String Str1) {
        table.getModel().setValueAt(Str, 0, 1);
        table.getModel().setValueAt(Str1, 1, 1);
    }

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

    private class MyTask extends SwingWorker<Void, Integer> {

        private String str;
        private String namePr;

        MyTask(String str) {
            this.str = str;
            addPropertyChangeListener(new SwingWorkerCompletionWaiter(str, namePr));
        }

        @Override
        protected Void doInBackground() throws Exception {
            if (str.equals("StartShedule")) {
                changeTableValues();
            }
            return null;
        }

        @Override
        protected void process(List<Integer> progress) {
        }

        @Override
        protected void done() {
            if (str.equals("StartShedule")) {
            }
        }
    }

    private class SwingWorkerCompletionWaiter implements PropertyChangeListener {

        private String str;
        private String namePr;

        SwingWorkerCompletionWaiter(String str, String namePr) {
            this.str = str;
            this.namePr = namePr;
        }

        SwingWorkerCompletionWaiter(String namePr) {
            this.namePr = namePr;
        }

        @Override
        public void propertyChange(PropertyChangeEvent event) {
            if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
                System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
                System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
            } else {
                System.out.println("SomeThing Wrong happends with Thread Status with Name :" + str);
            }
        }
    }
}

РЕДАКТИРОВАТЬ:

добавлено повторное генерирование из Future#get() в методе done() (по отличному предложению @takteek)

@Override
    protected void done() {
        if (str.equals("StartShedule")) {
            try {
                get();
                //errLabel.setText(String.valueOf(get()));
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            } catch (ExecutionException ee) {
                ee.printStackTrace();
            }catch (IllegalStateException is) {
                is.printStackTrace();
            }
        }
    }

но вывод по-прежнему и только Got exception, было бы так сложно получить какие-либо исключения из этого BlackBox

run:
Got exception
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
Got exception
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
Got exception
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
Got exception
Thread Status with Name :StartShedule, SwingWorker Status is STARTED
Thread Status with Name :StartShedule, SwingWorker Status is DONE
BUILD SUCCESSFUL (total time: 13 seconds)

person mKorbel    schedule 13.08.2011    source источник


Ответы (2)


Я не уверен, что это много добавляет, но я получил ожидаемое Caused by, используя вариант takteek ответ показан ниже. Я запустил его из командной строки, чтобы убедиться, что IDE не «помогает».

$ java -cp build/classes TableWithExecutor
StartShedule: PENDING -> STARTED
java.util.concurrent.ExecutionException: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 2
    at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)
    at java.util.concurrent.FutureTask.get(FutureTask.java:83)
    at javax.swing.SwingWorker.get(SwingWorker.java:582)
    at TableWithExecutor$MyTask.done(TableWithExecutor.java:103)
    at javax.swing.SwingWorker$5.run(SwingWorker.java:717)
    at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:814)
    at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:95)
    at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:824)
    at javax.swing.Timer.fireActionPerformed(Timer.java:291)
    at javax.swing.Timer$DoPostEvent.run(Timer.java:221)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:677)
    at java.awt.EventQueue.access$000(EventQueue.java:85)
    at java.awt.EventQueue$1.run(EventQueue.java:638)
    at java.awt.EventQueue$1.run(EventQueue.java:636)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:647)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:296)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:211)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:196)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:188)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 2
    at java.util.Vector.get(Vector.java:694)
    at TableWithExecutor.changeTableValues(TableWithExecutor.java:64)
    at TableWithExecutor.access$100(TableWithExecutor.java:14)
    at TableWithExecutor$MyTask.doInBackground(TableWithExecutor.java:92)
    at TableWithExecutor$MyTask.doInBackground(TableWithExecutor.java:80)
    at javax.swing.SwingWorker$1.call(SwingWorker.java:277)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at javax.swing.SwingWorker.run(SwingWorker.java:316)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:680)
StartShedule: STARTED -> DONE

Полный код:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.*;
import javax.swing.table.*;

/** @see https://stackoverflow.com/questions/7054627 */
public class TableWithExecutor extends JFrame {

    private static final int delay = 1000;
    private static final DateFormat df = DateFormat.getTimeInstance();
    private String[] columnNames = {"Product", "Availability"};
    private Object[][] data = {columnNames, columnNames, columnNames};
    private DefaultTableModel model;
    private JTable table;
    private Executor executor = Executors.newCachedThreadPool();
    private Timer timer;

    public TableWithExecutor() {
        model = new DefaultTableModel(data, columnNames);
        table = new JTable(model) {

            @Override
            public Class getColumnClass(int column) {
                return getValueAt(0, column).getClass();
            }
        };
        table.setDefaultRenderer(Date.class, new DefaultTableCellRenderer() {

            @Override
            protected void setValue(Object value) {
                setText((value == null) ? "" : df.format(value));
            }
        });
        table.setPreferredScrollableViewportSize(new Dimension(200, 100));
        JScrollPane scrollPane = new JScrollPane(table);
        add(scrollPane, BorderLayout.CENTER);
        timer = new Timer(delay, startCycle());
        timer.setRepeats(true);
        timer.start();
    }

    private Action startCycle() {
        return new AbstractAction(MyTask.STARTSCHEDULE) {

            @Override
            public void actionPerformed(ActionEvent e) {
                executor.execute(new MyTask(MyTask.STARTSCHEDULE));
            }
        };
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                TableWithExecutor frame = new TableWithExecutor();
                frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    private class MyTask extends SwingWorker<List<DateRecord>, DateRecord> {

        private static final String STARTSCHEDULE = "StartSchedule";
        private String name = STARTSCHEDULE;

        MyTask(String name) {
            this.name = name;
            addPropertyChangeListener(new TaskListener(name));
        }

        @Override
        protected List<DateRecord> doInBackground() throws Exception {
            for (int row = 0; row < model.getRowCount(); row++) {
                Date date = new Date();
                date.setTime(date.getTime() + row * 1000);
                publish(new DateRecord(row, date));
            }
            return null;
        }

        @Override
        protected void process(List<DateRecord> chunks) {
            for (DateRecord dr : chunks) {
                model.setValueAt(dr.date, dr.rowNumber, 1);
            }
        }

        @Override
        protected void done() {
            try {
                get();
            } catch (Exception e) {
                e.printStackTrace(System.err);
            }
        }
    }

    private static class DateRecord {

        private int rowNumber;
        private Date date;

        public DateRecord(int recordNumber, Date date) {
            this.rowNumber = recordNumber;
            this.date = date;
        }
    }

    private static class TaskListener implements PropertyChangeListener {

        private String name;

        TaskListener(String name) {
            this.name = name;
        }

        @Override
        public void propertyChange(PropertyChangeEvent e) {
            System.out.println(name + ": "
                + e.getOldValue() + " -> " + e.getNewValue());
        }
    }
}
person trashgod    schedule 14.08.2011
comment
там про правильно именуемый процесс (самое главное) и то только менять protected Void doInBackground() throws Exception {...} на стандартный блок try -> catch -> finally, дальше чудом работает, спасибо +1 - person mKorbel; 14.08.2011
comment
@Eng.Fouad: я должен упомянуть, что этот пример был предназначен для отображения ошибки. Обратите внимание, что он (неправильно) обновил графический интерфейс с doInBackground(). Я обновил код соответственно. - person trashgod; 26.08.2011
comment
хм... вы получаете доступ к модели в фоновом потоке, это достаточно безопасно? (Интересно об этом в моем недавнем вопросе :-) - person kleopatra; 08.03.2012
comment
@kleopatra: Хороший вопрос. Это работает только потому, что DefaultTableModel использует Vector, который синхронизирован. TableModel с некоторой другой внутренней структурой данных должен был бы принять другие меры. - person trashgod; 08.03.2012

Я думаю, что проблема, с которой вы столкнулись, заключается в том, что исключения, пойманные в фоновом потоке, выбрасываются повторно только в том случае, если вы вызываете get() после завершения обработки. Кажется, это общая проблема с SwingWorker.

Вы можете изменить свою функцию done() на:

@Override
protected void done() {
    if (str.equals("StartShedule")) {
       try {
          get();
       }
       catch (Exception ex) {
          // This exception was thrown during processing
          ex.printStackTrace();
       }
    }
}

done() выполняется в потоке отправки событий, поэтому вы можете отображать любые сообщения об ошибках, которые вам нужны. Я надеюсь, что это поможет некоторым.

person takteek    schedule 13.08.2011
comment
спасибо, верно, но какой/какой метод возвращает полный printStackTrace(), потому что теперь я вижу только Got exception, если существуют исключения(я) - person mKorbel; 14.08.2011
comment
хммм, может быть, я ищу не в тех местах, не знаю, как получить полную printStackTrace(); кстати +1 - person mKorbel; 14.08.2011
comment
это абсолютно то, что вы всегда должны делать при использовании SwingWorker. Я думаю, что документы должны лучше освещать этот факт. Исключением считается конечный результат рабочего потока. Это означает, что SwingWorker‹void, void›, который не возвращает никакого объекта или значения, тем не менее должен быть проверен на результат с помощью метода get(), который повторно вызовет исключение, возникшее в рабочем потоке. Помните, что исключения идут вверх по стеку, но в многопоточном приложении для каждого потока существует свой стек. - person AgostinoX; 14.08.2011
comment
@AgostinoX спасибо за комментарий, да, это было бы наполовину правдой, в случае этого вы попробуете пример от (@trashgod) с удалением исключения throws и переносом пустот или классов внутрь try - catch bloks, тогда Future вернет исключение (я), или если у вас есть другие проблемы с SwingWorker‹void, void›, то не стесняйтесь и опубликуйте это здесь в качестве ответа, потому что теперь у меня есть некоторое представление (возможно, с затененным изображением) об исправлениях, поступающих из Java1.6_021 - person mKorbel; 15.08.2011