Can't Get Arrayindexoutofboundsexception from Future≪≫ and Swingworker If Thread Starts Executor

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 via SwingWorker mechanism.
  • I believe that the instantiation of the ExecutorService is hidden as it is part of SwingWorker implementation and you may not edit it.
  • But at the same time, since SwingWorker implements Runnable, you can submit it to your own ExecutorService instance.
  • If you call execute() method, then it will be scheduled to it's own ExecutorService. Instead, you can manually submit the SwingWorker instance to your own ExecutorService.

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



Leave a reply



Submit