Contenido de certificación
Buscar
Social
Ofertas laborales ES
« Nuevo Seminario de Preparación para Java SE 6 Programmer | Main | Serialización de objetos en Java. »
jueves
mar292012

Java puzzle: la inexplicable ConcurrentModificationException

Al ejecutar este código, cambiar cualquier elemento de la segunda columna  de la tabla (la que emplea un editor diferente del estándar) y volver a hacer clic en la primera columna se lanza una ConcurrentModificationException. Sin embargo, el código parece emplear sincronización de modo correcto para proteger la lista que lanza la excepción.  ¿Cuál es el problema con este código?. Las respuestas en los comentarios, por favor :)

package examen;

import java.awt.*;
import java.util.*;
import java.util.List;

import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.table.*;

public class ProblemaConcurrencia extends JFrame {

    public ProblemaConcurrencia() {

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Object[][] datos = { {"Mary", "Esquiar", }, {"Lucas", "Esquiar"},
                           {"Kathya", "Escalar"}, {"Marcus", "Andrews",
                           "Correr"}, {"Angela", "Nadar"}
        };

        String[] columnNames = {"Nombre", "Pasatiempo"};

        DefaultTableModel tableModel = new DefaultTableModel(datos, columnNames);
        JTable tabla = new JTable(tableModel);
        tabla.setRowHeight(20);
        TableColumnModel tablaColumnModel = tabla.getColumnModel();

        tablaColumnModel.getColumn(1).setCellEditor((new
                TableCellEditorJComboboxHobbbies()));

        JScrollPane scrollPane = new JScrollPane(tabla);
        tabla.setPreferredScrollableViewportSize(new Dimension(500, 70));
        getContentPane().add(scrollPane, BorderLayout.CENTER);

    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            public void run() {

                ProblemaConcurrencia frame = new ProblemaConcurrencia();
                frame.pack();
                frame.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}


class TableCellEditorJComboboxHobbbies extends JComboBox implements
        TableCellEditor {

    private static String[] opciones = {"Esquiar", "Patinar", "Correr", "Nadar",
                                       "Escalar"};
    private final List listeners = new ArrayList<
            CellEditorListener>();

    public TableCellEditorJComboboxHobbbies() {
        super(opciones);
    }

    public Component getTableCellEditorComponent(JTable table, Object value,
                                                 boolean isSelected, int row,
                                                 int column) {
        this.setSelectedItem(value);
        return this;
    }

    public Object getCellEditorValue() {
        return this.getSelectedItem();
    }

    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }

    public boolean shouldSelectCell(EventObject anEvent) {
        return true;
    }

    public boolean stopCellEditing() {
        synchronized (listeners) {
            for (CellEditorListener l : listeners) {
                l.editingStopped(null);
            }
        }
        return true;
    }

    public void cancelCellEditing() {
        synchronized (listeners) {
            for (CellEditorListener l : listeners) {
                l.editingCanceled(null);
            }
        }
    }

    public void addCellEditorListener(CellEditorListener l) {
        synchronized (listeners) {
            listeners.add(l);
        }
    }

    public void removeCellEditorListener(CellEditorListener l) {
        synchronized (listeners) {
            listeners.remove(l);
        }
    }
}

Reader Comments (3)

El problema esta en la implementacion del listener. Deberias utilizar CopyOnWriteArrayList en vez de ArrayList.


private final java.util.List<CellEditorListener> listeners = new CopyOnWriteArrayList<CellEditorListener>();

marzo 29, 2012 | Unregistered CommenterMauro Monti

Al pasar a la otra celda se invoca stopCellEditing, que obtiene un candado sobre la lista y la recorre para invocar editingStopped en cada listener. Si desde ese método se invoca a removeCellEditorListener, el candado que está ahí dentro ya se tiene (porque se manejan a nivel hilo) de modo que se ejecuta de inmediato el listeners.remove y entonces en la siguiente iteración dentro de stopCellEditing se arroja la excepción.

marzo 29, 2012 | Unregistered CommenterEnrique Zamudio

Premio a los dos :) el problema está bien descrito por Enrique, y la solución de Mauro probablemente sea la ideal.

marzo 30, 2012 | Registered CommenterAbraham

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>