Sincronizar arraylist

Pessoal estava mexendo com threads e surgiu um problema que acredito ser de sincronização das thread que consomem um arraylist, pesquisei no google e cheguei ao seguinte código

[code]
/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */

package teste;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
*

  • @author rodrigo
    */
    public class Teste {

    public ArrayList array;

    public Teste(){
    array = new ArrayList();
    for(int i =0; i< 10000; i++){
    array.add(i);
    }
    Teste1 t1 = new Teste1();
    t1.nome = “T1”;
    Teste1 t2 = new Teste1();
    t2.nome = “T2”;
    Teste1 t3 = new Teste1();
    t3.nome = “T3”;

     Thread th1 = new Thread(t1);
     Thread th2 = new Thread(t2);
     Thread th3 = new Thread(t3);
    
     th1.start();
     th2.start();
     th3.start();
     System.out.println(array);
    

    }

    public static void main(String args[]){
    Teste t = new Teste();
    }

    class Teste1 implements Runnable{
    public String nome;
    public void run(){
    List list = Collections.synchronizedList(array);

         Iterator i = list.iterator();
         while (i.hasNext()) {
             int valor = (Integer) i.next();
             i.remove();
             System.out.println(nome+" removeu = "+valor);
         }
      }
     }
    

    }[/code]

porém ele ainda esta dando o erro de Exception in thread “main” java.util.ConcurrentModificationException

alguém poderia me dar uma luz?

usa Vector =)
até onde eu sei, Vector é praticamente o mesmo que ArrayList porém, com todos os métodos synchronized.
:smiley:

troquei para Vector melhorou mas continua ocorrendo a exception, com menos frequencia mas continua

posta o seu código aqui pra gente dar uma olhada. :smiley:
quem sabe fique melhor pra ajudarmos… :wink:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package teste;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

/**
 *
 * @author rodrigo
 */
public class Teste {

    public Vector array;

    public Teste(){
        array = new Vector();
        for(int i =0; i< 10000; i++){
            array.add(i);
        }
        Teste1 t1 = new Teste1();
        t1.nome = "T1";
        Teste1 t2 = new Teste1();
        t2.nome = "T2";
        Teste1 t3 = new Teste1();
        t3.nome = "T3";

        Thread th1 = new Thread(t1);
        Thread th2 = new Thread(t2);
        Thread th3 = new Thread(t3);

        th1.start();
        th2.start();
        th3.start();
    }

    public static void  main(String args[]){
        Teste t = new Teste();
    }


    class Teste1 implements Runnable{
        public String nome;
        public void  run(){

            Iterator i = array.iterator();
            while (i.hasNext()) {
                int valor = (Integer) i.next();
                i.remove();
                System.out.println(nome+" removeu = "+valor);
            }
         }
        }
    }

O problema não tem nada haver com threads que você criou.

Para provar isso, coloque esse tracho de código em uma função normal:

 Iterator i = array.iterator();  
             while (i.hasNext()) {  
                 int valor = (Integer) i.next();  
                 i.remove();  
                 System.out.println(nome+" removeu = "+valor);  
             }  

O problema é que você está removendo elementos da lista enquanto você percorre ela (através do iterator) !
Você não pode fazer isto (não do jeito que você está fazendo aí)!

você poderia usar um for e remover os elementos através dele…

for (int i = 0; i<suaLista.size(); i++){
remove(i);
}

Eu acho que isso funcionaria, mas não tenho certeza.

Abraços
<><

cara… não consegui descobrir ainda o porque… mas usando o Iterator tava dando o erro, mesmo fazendo o bloco synchronized :frowning:
mas mudei seu método run, ai não deu mais a Exception…

[code]public void run() {

for (int i = 0; i < array.size(); i++) {
	int valor = (Integer) array.get(i);
	System.out.println(nome + " removeu = " + valor);
}

}[/code]

ainda vou fuçar mais um pouco pra ver se descubro o porque de estar dando a Exception. :smiley:

[]´s

vc resolveu o problema antes de eu postar a solução ¬¬

Abraços
<><

[quote=Djonatah]O problema não tem nada haver com threads que você criou.

O problema é que você está removendo elementos da lista enquanto você percorre ela (através do iterator) !
Você não pode fazer isto (não do jeito que você está fazendo aí)!

você poderia usar um for e remover os elementos através dele…

[/quote]

Pelo contrário. O Pattern Iterator serve justamente para remover elementos de uma lista enquanto se percorre ela; se você fizer

List<String> lista = new ArrayList<String>();
//adiciona vários elementos
for(int i=0; i<list.size(); i++)
    lista.remove(i);

Você continuará com metade dos elementos nela após a saída do for, pois o elemento seguinte ao removido tem seu índice diminuído em um, conseqüentemente sendo pulado. E o problema tem tudo a ver com threads, por que você acha que o nome da exceção é “ConcurrentModificationException”?!?

Você tirou a linha que remove um elemento. Não há problema em percorrer a mesma lista várias vezes concorrentemente se você não altera ela.

A maneira correta de fazer esta iteração removendo elementos acredito que seria

 class Teste1 implements Runnable{  
         public String nome;  
         public void  run(){  
        
             synchronized(array) {
                 Iterator i = list.iterator();  
                 while (i.hasNext()) {  
                     int valor = (Integer) i.next();  
                     i.remove();  
                     System.out.println(nome+" removeu = "+valor);  
                 }  
              }  
         }  
     } 
} 

Mas não sei qual o motivo de percorrer uma lista 3 vezes se o objetivo é simplesmente esvaziar ela :stuck_out_tongue:

ops… mals ae… :oops: é verdade…esqueci de por o comando de remoção. :frowning:

[quote=MarceloS][quote=Djonatah]O problema não tem nada haver com threads que você criou.

O problema é que você está removendo elementos da lista enquanto você percorre ela (através do iterator) !
Você não pode fazer isto (não do jeito que você está fazendo aí)!

você poderia usar um for e remover os elementos através dele…

[/quote]

Pelo contrário. O Pattern Iterator serve justamente para remover elementos de uma lista enquanto se percorre ela; se você fizer


[/quote]

Hmmm eu não sabia disso, na verdade eu fiz uma associação com a alteração atavés do enhanced for loop! foi por isso que confundi ;).

valeu pelo conhecimento!

Abraços
<><

Cara valeu usando o synchronized(array) funcionou, muito obrigado a atenção de todos, e na verdade este é só um exemplo para resolver esse problema. Na verdade tenho que ir pegando elementos processo-los e remove-los da lista, como nesse processamento tenho que acessar outra maquina, achei melhor dividir em várias threads.

[quote=jhonatandarosa]usa Vector =)
até onde eu sei, Vector é praticamente o mesmo que ArrayList porém, com todos os métodos synchronized.[/quote]

Não. Não se deve usar Vector, nem nesse caso. Você pode gerar uma versão sincronizada do arraylist assim:

List<String> lista = Collections.syncronizedList(outraLista);

E isso ele já fazia.

Não é isso não.

O problema é aqui é o seguinte. Não se pode remover um elemento durante uma iteração, por fora da iteração. O iterator guarda internamente o tamanho da lista que está percorrendo, e atualiza esse contador no método remove do iterator. Caso o tamanho da lista seja diferente do contador, uma concurrent modification exception é disparada. Isso ocorre, por exemplo, se o método remove da lista foi chamado, já que este não atualiza o contador do iterator. Dessa forma o iterator percebe que a lista foi modificada externamente (de maneira “concorrente”) e a exceção é disparada. Assim, não necessariamente está associada a multiplas threads, observe:

[code]
//Considere a existência do objeto lista do tipo List contendo alguns elementos.

public void remove() {
lista.remove(0);
}

public void percorre() {
Iterator itr = lista.iterator();
while (it.hasNext()) {
it.remove(); //ok, remoção pelo iterator
remove(); //vai gerar concurrent modification exception
}[/code]

No código acima, uma única thread gera a ConcurrentModificationException.

Observe que no programa postado pelo autor do tópico, as multiplas threads removiam objetos de fora do iterator uma da outra, gerando a exception. A sua solução foi sincronizar o bloco todo, garantindo assim que só uma thread faça a iteração e a remoção por vez.

Entretanto, se você realmente precisasse explorar paralelismo, essa solução estaria efetivamente serializando as threads. Para contornar esse problema, o pacote java.util.concurrent fornece o CopyOnWriteArrayList, que garante que um ConcurrentModificationException não será lançado.

Uma lição importante aqui: Vector está obsoleto desde a versão 1.2 do Java. Esqueça dele, há sempre alternativas melhores na API de coleções.

Desculpe minha ignorância… mas onde está escrito que o Vector foi considerado obsoleto a partir da versão 1.2 :?:

[]'s

Veja as coleções citadas pelo trail da Sun:
http://java.sun.com/docs/books/tutorial/collections/implementations/index.html

O Vector não aparece na lista de coleções atuais, e é citado como “Legacy Collection”. Note que nas páginas sobre as coleções específicas, nem o Vector e nem o HashTable sequer são citados.

Os livros Core Java e Effective Java também ressalta a informação.

Há mais motivos para não se usar vector:

  1. Possuem métodos de funcionalidade duplicada, fora da especificação da interface list. Tome muito cuidado para não usar esses métodos, ou será difícil de trocar de coleção depois. Por exemplo, addElement (no lugar de add);
  2. Os métodos de nome longo tornam o código mais difícil de ler;
  3. Força sincronização em absolutamente todos os trechos de código, mesmo naqueles que a sincronização externa já é utilizada;
  4. Por padrão, dobra de tamanho sempre que seu limite máximo é atingido, do contrário do ArrayList, que só se expande em 50%. Claro, dependendo do caso o primeiro comportamento pode ser melhor. Mas poucas vezes nos preocupamos com isso e, para regra geral, a do ArrayList é melhor.

A razão pela qual Vector não foi tornado 100% deprecated deve sido o Swing. Várias classes padrão usam Vector como base para suas implementações. O mesmo vale para o Hashtable, que deve ser substituído por HashMap.