Can't get ArrayIndexOutOfBoundsException from Future<?> and SwingWorker if thread starts Executor
I play with multitreading for SwingWorker by using Executor, and I'm there by mistake identified wrong elements from the Vector, looks like as this code pretty ignores that element in Vector doesn't exist
my question -> how to/is possible to catch this exception(s) some way
simple output
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)
by uncomment
//changeTableValues1(); // un-comment for get ArrayIndexOutOfBoundsException
everything is correct, I get ArrayIndexOutOfBoundsException and output is
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)
from code
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.get开发者_运维百科Model().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);
}
}
}
}
EDIT:
added re-thrown from Future#get() in done() method (by @takteek excellent suggestion)
@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();
}
}
}
but output is still and only Got exception
, would be so hard to get any exception(s) from this 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)
I'm not sure it adds much, but I got the expected Caused by
using the variation of takteek's answer shown below. I ran it from the command line to be sure the IDE wasn't "helping".
$ 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
Full code:
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());
}
}
}
I think the problem you're running into is that exceptions caught in the background thread are only re-thrown if you call get()
when processing is complete. This seems to be a common problem with SwingWorker.
You can change your done()
function to:
@Override
protected void done() {
if (str.equals("StartShedule")) {
try {
get();
}
catch (Exception ex) {
// This exception was thrown during processing
ex.printStackTrace();
}
}
}
done()
gets executed on the event dispatch thread so you can display any error messages you need to from there. I hope that helps some.
精彩评论