Problemas com Threads e Lists (java.util.ConcurrentModificationException)

Boa noite.

Eu estou tentando implementar um código mas está dando o seguinte erro:

java.util.ConcurrentModificationException
	at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
	at java.util.AbstractList$Itr.next(Unknown Source)
	at td.gui.MapHolder.processAI(MapHolder.java:27)
	at td.gui.MapView.processPiecesAI(MapView.java:38)
	at td.gui.TDGame.processLogics(TDGame.java:64)
	at jgf.core.MainLoop.skipFramesInExcessTime(MainLoop.java:204)
	at jgf.core.MainLoop.run(MainLoop.java:156)
	at java.lang.Thread.run(Unknown Source)

No caso eu tenho essa clase MapHolder que tem um List que da erro quando adiciono algo novo e chega no momente de iterar novamente.

List<Piece> pieces = new ArrayList<Piece>();
public void add(Piece piece){
pieces.add(piece);
}

public void processAI(){
for(Piece piece : pieces)
piece.processAI();
}

Mas o que acontece é que em um dos chamados ao piece.processAI() o método add é chamado e faz com que de um erro na iteração da lista.

Como posso resolver isso?

Você não pode iterar em uma lista e dentro da iteração fazer modificação na lista.

Uma solução seria fazer uma cópia da lista e iterar usando a cópia e não a lista original.

Obrigado isso funcionou, eu me amtei hoje a tarde com isso eu imaginei fazer isso mas pensei que os objetos da List também seriam clonados. Falta minha de ver na API.
=/

Só para reforçar: Nem precisa de multiplas threads para o problema ocorrer.

O simples fato de vc iterar e remover um elemento, mesmo na mesma thread, pode causar o problema.

List<String> lista = new ArrayList<String>(); lista.add("a"); lista.add("b"); lista.add("c"); for (String letra : lista) { if (letra.equals("b")) lista.remove(letra); //ConcurrentModificationException, estamos num for each! }

Só se pode remover da lista com um iterator. No caso de jogos, nunca faça um agente seu remover outro diretamente. Marque alguma propriedade como estaMorto() como true, e então faça uma iteração só para remover os mortos, depois do processamento da lógica. :wink:

Nem todas as coleções lançam ConcurrentModificationException mas isso vem à custa de outras coisas.
Se existe uma necessidade vital de adicionar ou remover coisas da mesma lista em que estamos iterado podemos usar as novas (não tão novas assim) coleções CopyOnWrite (CopyOnWriteArrayList e CopyOnWriteArraySet). Elas produzem um iterador que não sofre com ConcurrentModificationException , mas à custa de copiar todo o array internamente quando se dá um add ou remove.

No caso do remove ha uma forma amigável de não causar ConcurrentModificationException , utilizar o método remove() do iterador.
Isso é sempre garantido que não causará ConcurrentModificationException e não implica em criar copias da coleção ( o que é sempre ruim)

Amigos estou com quase o mesmo problema.

O problema é que não estou modificando a lista original em momento algum, não vejo onde está o erro, pediria por favor se alguém puder me dar alguma dica de como resolver isso.

Vou passar o código e a exception.

Código:

[code]Thread t2 = new Thread() {

@Override
public void run() {
    getDadosOrdemStandard(codigoOs);
    beanOS = new OrdemServicoDAO(c).getOrdemServico(codigoOs, beanOS);
    SwingUtilities.invokeLater(new Runnable() {

        public void run() {
            if (config.isOSsemBaixa()) {
                List listaServico = beanOS.getListaServicos();
                Iterator it = listaServico.iterator();
                boolean fechou = false;
                while (it.hasNext()) {
                    Servico servico = (Servico) it.next(); // O erro está nessa linha: 940
                    Date dataHoraBaixaProducao = new Date();
                    try {
                        sql = "INSERT INTO ORDEM_BAIXA(" +
                                "   CODIGO_ORDEM," +
                                "   DATA_BAIXA," +
                                "   HORA_BAIXA, " +
                                "   QUANTIDADE_PRODUZIDA, " +
                                "   QUANTIDADE_PERDIDA, " +
                                "   FUNCIONARIO, " +
                                "   CODIGO_SERVICO) " +
                                "VALUES(?, ?, ?, ?, ?, ?, ?);";
                        int idx = 0;
                        ps = c.prepareStatement(sql);
                        ps.setObject(++idx, codigoOs);
                        ps.setObject(++idx, new java.sql.Date(dataHoraBaixaProducao.getTime()));
                        ps.setObject(++idx, new java.sql.Time(dataHoraBaixaProducao.getTime()));
                        ps.setObject(++idx, servico.getQuantidade());
                        ps.setObject(++idx, 0.0);
                        ps.setObject(++idx, Global.userBean.getNome());
                        ps.setObject(++idx, servico.getCodigo());
                        ps.execute();

                        sql = "UPDATE CONTROLE_ORDEM SET " +
                                "   FOLHAS_PERDIDAS = ?, " +
                                "   VALOR_FINAL = ?," +
                                "   QUANTIDADE_PRODUZIDA=? " +
                                "WHERE " +
                                "   CODIGO_ORDEM = ? AND " +
                                "   CODIGO_SERVICO = ?";
                        idx = 0;
                        ps = c.prepareStatement(sql);
                        ps.setObject(++idx, 0.0);
                        ps.setObject(++idx, servico.getValorCalculado());
                        ps.setObject(++idx, servico.getQuantidade());
                        ps.setObject(++idx, codigoOs);
                        ps.setObject(++idx, servico.getCodigo());
                        ps.executeUpdate();
                    } catch (SQLException ex) {
                        Lib.Warning(frame, "Adicionando Baixa na OS", ex.getMessage() + "\n\n" + T.getStackTrace(ex.fillInStackTrace()), 1);
                    }
                    fechou = true;
                } // while da LISTA dos Servicos
               
            }
        }
    });
}

};
t2.start();
[/code]

A Excetpion que ocorre:

java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(Unknown Source) at java.util.AbstractList$Itr.next(Unknown Source) at com.sikgraf.ui.OrcamentoOrdemServicoUI$17$1.run(OrcamentoOrdemServicoUI.java:940) at java.awt.event.InvocationEvent.dispatch(Unknown Source) at java.awt.EventQueue.dispatchEvent(Unknown Source) at com.sikgraf.lib.contextmenu.MyEventQueue.dispatchEvent(MyEventQueue.java:29) at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.awt.EventDispatchThread.run(Unknown Source)

Obrigado
Francisco

Provavelemente há um erro na implementação desse iterador. Essa aí não deve ser uma das listas padrão das coleções do java. Então, o código dela terá que ser analisado para saber o que tem de errado aí.

Obrigado pela resposta amigo, mas não tem nada errado com a montagem da lista.

ArrayList<Servico> listaServico = beanOS.getListaServicos();

Segue como a lista é criada:

[code]ArrayListModel servicosOrdem = new ArrayListModel();
while (rs.next()) {
Servico servico = new Servico();
servico.setCodigo(rs.getInt(“CODIGO”));
servico.setCodigoTabelaServicoOrcamento(rs.getInt(“CODIGO_TAB_SERVICO”));
servico.setTituloOrcamento(rs.getString(“TITULO_ORCAMENTO”));
servico.setValorMinimo(rs.getDouble(“VALOR_MINIMO”));
servico.setMedidaMinima(rs.getDouble(“MEDIDA_MINIMA”));
servico.setValorMilheiro(rs.getDouble(“VALOR_MILHEIRO”));
servico.setReserva(rs.getInt(“RESERVA”));
servico.setAcertos(rs.getInt(“TOTAL_ACERTOS”));
servico.setValorTela(rs.getDouble(“VALOR_TELA”));
servico.setNome(rs.getString(“NOME”));
servico.setNomeServicoTabela(rs.getString(“NOME_SERVICO_TABELA”));
servico.setUnidade(rs.getString(“UNIDADE”));
servico.setQuantidade(rs.getDouble(“QUANTIDADE”));
servico.setComprimento(rs.getDouble(“COMPRIMENTO”));
servico.setLargura(rs.getDouble(“LARGURA”));
servico.setMedida(rs.getDouble(“MEDIDA”));
servico.setValorUnitario(rs.getDouble(“VALOR_UNITARIO”));
servico.setValorCalculado(rs.getDouble(“VALOR_CALCULADO”));
servico.setValorFinal(rs.getDouble(“VALOR_FINAL”));
servico.setQuantidadeFinal(rs.getDouble(“QUANTIDADE_PRODUZIDA”));
servico.setLados(rs.getInt(“LADOS”));
servico.setAliquotaIP(rs.getDouble(“ALIQUOTA_IPI”));
servico.setValorIPI(rs.getDouble(“VALOR_IPI”));
servico.setValorMetroQuadrado(rs.getDouble(“VALOR_M2”));
servico.setAnotacao(rs.getString(“ANOTACAO”));
servico.setServicoNormal(rs.getInt(“COM_VERNIZ”) == 0);
servico.setPercentualVerniz(rs.getDouble(“PERCENTUAL_VERNIZ”));
PreparedStatement ps1 = conn.prepareStatement("" +
"SELECT " +
" CODIGO, " +
" CODIGO_TABELA, " +
" INICIO, " +
" FIM, " +
" VALOR_SERVICO, " +
" VALOR_CHAPA " +
"FROM " +
" TABELA_SERVICO_INTERVALO " +
“WHERE " +
" CODIGO_TABELA=?;”);
ps1.setInt(1, rs.getInt(“CODIGO_TAB_SERVICO”));
ResultSet rs1 = ps1.executeQuery();
ArrayList intervalos = new ArrayList();
while (rs1.next()) {
IntervaloFormBean intervalo = new IntervaloFormBean();
intervalo.setCodigo(rs1.getInt(“CODIGO”));
intervalo.setCodigoTabela(rs1.getInt(“CODIGO_TABELA”));
intervalo.setInicio(rs1.getInt(“INICIO”));
intervalo.setFim(rs1.getInt(“FIM”));
intervalo.setValorServico(rs1.getDouble(“VALOR_SERVICO”));
intervalo.setValorChapa(rs1.getDouble(“VALOR_CHAPA”));
intervalos.add(intervalo);
}
rs1.close();
ps1.close();
servico.setIntervalos(intervalos);

ArrayList<BaixaProducao> listaBaixas = this.getBaixasServicoOrdem(numeroOrdem, servico.getCodigo());
servico.setListaBaixas(listaBaixas);

servicosOrdem.add(servico);

}
bean.setListaServicos(servicosOrdem);[/code]

Sua lista não é da classe ArrayList, e sim da classe ArrayListModel.

Que classe é essa? Onde está o código dela?

Desculpe a confusão (mas isso foi necessário), no método set do bean a lista de ArrayListModel para a ser ArrayList, esse também não é o problema.

[code] public ArrayList getListaServicos() {
return listaServicos;
}

public void setListaServicos(ArrayListModel<Servico> listaServicos) {
    this.listaServicos.clear();
    this.listaServicos.addAll(listaServicos);
    changeSupport.firePropertyChange("listaServicos", this.listaServicos, this.listaServicos);
}

[/code]

Quantas vezes esse thread é disparado? Não corre o risco de duas threads simultâneas estarem chamado add na mesma lista ao mesmo tempo?