Can't get ArrayIndexOutOfBoundsException from Future<?> and SwingWorker if thread starts Executor
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());
}
}
}
How to correctly use ExecutorService to manage the number of concurrently running SwingWorkers?
That moment when you think: It was so obvious!
ExecutorService executorService = Executors.newFixedThreadPool(20);
for (int i = 0; i < 500; i++) {
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
@Override
protected Boolean doInBackground() throws Exception {
System.out.println("One SwingWorker just ran!");
return true;
}
protected void done() {
boolean status;
try {
status = get();
} catch (InterruptedException e) {
// This is thrown if the thread's interrupted.
} catch (ExecutionException e) {
// This is thrown if we throw an exception
// from doInBackground.
}
}
};
executorService.submit(worker);
}
It works great!
ExecutorService and SwingWorker
How does SwingWorker
work with ExecutorService
?
- Swing maintains an internal
ExecutorService
instance which is used
to execute tasks that you submit viaSwingWorker
mechanism. - I believe that the instantiation of the
ExecutorService
is hidden as it is part ofSwingWorker
implementation and you may not edit it. - But at the same time, since
SwingWorker
implementsRunnable
, you can submit it to your ownExecutorService
instance. - If you call
execute()
method, then it will be scheduled to it's ownExecutorService
. Instead, you can manually submit theSwingWorker
instance to your ownExecutorService
.
If I submit a SwingWorker
task to an Executor, will that one task spawn up to ten threads?
- No. To understand this, you should go through
ExecutorService
documentation. One task will only use one thread (unless you
specifically program multithreaded task). - Other threads are kept in idle by
ExecutorService
. This consumes almost no CPU time. - If 10 GUI events occur simultaneously, each will be assigned to each of the available thread. If the thread is not required, it will not be running.
- If number of tasks is more than number of threads available, they will be scheduled in a queue by the
ExecutorService
itself.
Hope this clears the doubts. Usually it is a good idea to use default implementation (it works very well), but you easily can limit number of threads if you want. Also, it will not replace Swing's ExecutorService
. Since it is already there, it's best to use it instead of creating another.
Good Luck.
Differences between SwingWorker and Executor
1) SwingWorker
is created as bridge betweens Java Essentials Classes
and Swing by implements Future
, and quite guarantee that outoput from methods process
, publish
and done
will be on EventDispatchThread
2) you can invoke SwingWorker
from Executor
, this is most safiest methods how to create multithreading in Swing by implements SwingWorker
3) notice carefully with number or thread, because Executor
doesn't care somehow about SwingWorkers
life_cycle
4) another important notice and here
5) for listening states from SwingWorker
you have to implements PropertyChangeListener
The proper way to handle exceptions thrown by the SwingWorker.doInBackground
Therefore, if the get method causes a waiting within the done method, in fact it would block the Event Dispatch Thread, since the done method is executed on EDT.
Actually, if done is called, the doInBackground
has already returned, therefore calling get
within done
will NOT block the Event Dispatching Thread.
The same goes if you are using the PropertyChangeListener
support and monitoring the change in state to DONE
Updated
So, after having a look at SwingWorker calls 'done' before the 'doInBackground' is finished, which would mean that calling get
on a cancelled worker would block the EDT indeventially, the basic work around is actually to ignore the return result by checking the SwingWorker#isCancelled
state. Let's face it, if the worker is cancelled, the return result is unknown/undefined, so it's best NOT to try and get
it.
As an example (based on the code from the bug)
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
public class Main {
public static void main(String[] args) throws InterruptedException {
SwingWorker<String, String> worker = new SwingWorker<String, String>() {
@Override
protected String doInBackground() throws Exception {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Working...");
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
System.out.println("Got interrupted!");
}
try {
System.out.println("Cleaning up");
Thread.sleep(10000);
System.out.println("Done cleaning");
} catch (InterruptedException ex) {
System.out.println("Got interrupted second time!");
}
return null;
}
@Override
protected void done() {
System.out.println("Done");
if (!isCancelled()) {
long start = System.currentTimeMillis();
try {
get();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("Took " + ((end - start) / 1000d));
} else {
System.out.println("Was cancelled");
}
}
};
worker.execute();
Thread.sleep(10000);
worker.cancel(true);
Thread.sleep(20000);
}
}
Related Topics
How to Get Utf-8 Working in Java Webapps
How to Get the Current Date and Time in Utc or Gmt in Java
How to Make a Deep Copy of an Object
How to Find a Button Source in Awt (Calculator Homework)
Why Do This() and Super() Have to Be the First Statement in a Constructor
When Should I Use "This" in a Class
What Is a Nosuchbeandefinitionexception and How to Fix It
Compare Two Objects With .Equals() and == Operator
Recursively List Files in Java
How to List the Files Inside a Jar File
Want Current Date and Time in "Dd/Mm/Yyyy Hh:Mm:Ss.Ss" Format
What Do ^ and $ Mean in a Regular Expression
How Many Ways to Click on Webelement in Webdriver
How to Call One Constructor from Another in Java
Downloading a File from Spring Controllers
Access Restriction on Class Due to Restriction on Required Library Rt.Jar