tags:

views:

53

answers:

3

I'm trying to add items to a JList asynchronously but I am regularly getting exceptions from another thread, such as:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 8

Does anyone know how to fix this?

(EDIT: I answered this question because it's been bugging me and there is no clear search engine friendly way of finding this info.)

A: 

Are you perhaps modifying it from another thread? Could perhaps be modifying it in the same thread during execution of a JList (or related) method that expects the contents to remain the same size.

Tom Hawtin - tackline
+3  A: 

The model interface is not thread safe. You can only modify the model in EDT.

It is not thread safe because it asks the size separately from the contents.

iny
+4  A: 

Swing components are NOT thread-safe and may sometimes throw exceptions. JList in particular will throw ArrayIndexOutOfBounds exceptions when clearing and adding elements.

The work-around for this, and the preferred way to run things asynchronously in Swing, is to use the invokeLater method. It ensures that the asynchronous call is done when all other requests.

Example using SwingWorker (which implements Runnable):

SwingWorker<Void, Void> worker = new SwingWorker<Void, Void> () {
    @Override
    protected Void doInBackground() throws Exception {
        Collection<Object> objects = doSomethingIntense();
        this.myJList.clear();
        for(Object o : objects) {
            this.myJList.addElement(o);
        }
        return null;
    }
}

// This WILL THROW EXCEPTIONS because a new thread will start and meddle
// with your JList when Swing is still drawing the component
//
// ExecutorService executor = Executors.newSingleThreadExecutor();
// executor.execute(worker);

// The SwingWorker will be executed when Swing is done doing its stuff.
java.awt.EventQueue.invokeLater(worker);

Of course you don't need to use a SwingWorker as you can just implement a Runnable instead like this:

// This is actually a cool one-liner:
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        Collection<Object> objects = doSomethingIntense();
        this.myJList.clear();
        for(Object o : objects) {
            this.myJList.addElement(o);
        }
    }
});
Spoike