Concorrência em ArrayList

Olá Pessoal.
Antes de mais nada, quero agradecer a todos que postam aqui. Conheço o site a bastante tempo … já me ajudou de mais, e como apareceu uma dúvida mais complicada pra mim, resolvi me logar e postar …

Minhas situação é a seguinte:
Tenho um classe principal, na qual gero um array list de objetos.
Em outras classes, utilizo este array, fazendo o apontamento …
até aí tudo certo …

porém, estou fazendo concorrência neste array: tenho 01 classe, rodando em um thread que de forma aleatória insere objetos nessa lista, e outra classe que faz uma varredura constante (com um iterator) no array, pra colher informações (e mais na frente, vou precisar alterar valores).
aí, como é de se imaginar tenho uma java.util.concurrent.modification.exception.

Qual seria uma forma deu resolver este problema ? criar uma classe (com um método público statico) de acesso à este arraylist, sincronizando os acessos ? Seria isso mesmso ? e como eu faria isso ?
Fazendo essa classe, eu num estaria bloqueando o array inteiro … ? ao inves de que deveria ser bloqueado o acesso somente ao objeto deste array ? tou meio perdido … hihi … agradeço se alguem me orientar um pouco … =) valeu … muito obrigado por antecipação …

Use a classe Vector, ela tem seus métodos synchronised que evitam concorrência.

Mesmo usando Vector, uma alteração dos dados enquanto vocẽ estiver usando o Iterator ainda pode dar uma ConcurrentModificationException. Do Javadoc: “if the Vector is structurally modified at any time after the Iterator is created, in any way except through the Iterator’s own remove or add methods, the Iterator will throw a ConcurrentModificationException”.

Você poderia fazer uma iteração com for em cima do Vector… for (int i = 0; i < vector.size(); i++)… Isso se a precisão dos dados não for necessária… Você pode acabar não olhando todos os registros no final… Se for necessária, você pode criar um bloco synchronized(vector) antes de usar ele com o for ou com o iterator. Ai vc garante que ele não vai ser alterado enquanto estiver varrendo.

Olhar um pouco a documentação não machuca ninguém. Talvez

http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

  1. Vector não é a solução para os seus problemas porque ela não toma conta da concorrência. A única coisa que ela faz é proteger, através de um lock, um cursor interno que indica qual é o tamanho do Vector em relação ao array subjacente.

  2. Quando vejo alguém usando um ArrayList para ser compartilhado entre threads, e ainda por cima faz uma busca nele, é porque provavelmente está usando a estrutura de dados errada. Quais são as operações que você deve fazer sobre esse ArrayList? Dependendo, pode ser que você precise de um ConcurrentHashMap ou alguma outra estrutura que está no pacote java.util.concurrent.

Talvez esse tipo de List aqui não resolveria ?

http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html

Leia sobre a parte de um ArrayList concorrente através do método

Collections.synchronizedList method.

Só li por alto o seu problema.

Se for isso me avise.

Obrigado.

List&lt;Object&gt; myList = Collections.synchronizedList(new ArrayList&lt;Object&gt;())

Usa a api Collections do java.util q ele já gera uma lista thread safe pra você.

[quote=lele_vader]Collections.synchronizedList method.
[/quote]

Collections.synchronizedList é mais ou menos a mesma coisa que new Vector - só retorna uma estrutura de dados que contém um lock que protege a integridade interna, mas não dos dados em si.

Quer dizer que o Collections.synchronizedList, só vai garantir que você não modifique a lista, por exemplo adicionando ou removendo itens, porém não impede que as 2 threads mudem o mesmo objeto é isso ?

[quote=Frital]Olá Pessoal.
Antes de mais nada, quero agradecer a todos que postam aqui. Conheço o site a bastante tempo … já me ajudou de mais, e como apareceu uma dúvida mais complicada pra mim, resolvi me logar e postar …

Minhas situação é a seguinte:
Tenho um classe principal, na qual gero um array list de objetos.
Em outras classes, utilizo este array, fazendo o apontamento …
até aí tudo certo …

porém, estou fazendo concorrência neste array: tenho 01 classe, rodando em um thread que de forma aleatória insere objetos nessa lista, e outra classe que faz uma varredura constante (com um iterator) no array, pra colher informações (e mais na frente, vou precisar alterar valores).
aí, como é de se imaginar tenho uma java.util.concurrent.modification.exception.

Qual seria uma forma deu resolver este problema ? criar uma classe (com um método público statico) de acesso à este arraylist, sincronizando os acessos ? Seria isso mesmso ? e como eu faria isso ?
Fazendo essa classe, eu num estaria bloqueando o array inteiro … ? ao inves de que deveria ser bloqueado o acesso somente ao objeto deste array ? tou meio perdido … hihi … agradeço se alguem me orientar um pouco … =) valeu … muito obrigado por antecipação …[/quote]

Depende muito do real proposito do seu algoritmo. à prioria podemos dizer que não use ArrayList ( nem Vector) e sim Collections.synchronizedList(new ArrayList());
Mas muito provavelmente vc precisa de um Queue, mais especificamente um BlockingQueue.

Essa varredura parece muito suspeita, assim como a alteração. Sem mais detalhes do algoritmo é dificil de dizer. A lista sincronizada já vai ajudar, mas acho que se vc explicar melhor o algoritmo, podemos ajudar mais.

Se a classe mexe pouco na estrutura da lista (inserir/remover elementos), use um CopyOnWriteArrayList:
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CopyOnWriteArrayList.html

Caso contrário, vai ter que sincronizar os métodos que usam o List. Mas só testando mesmo para ver o que é mais eficiente.

Em se tratando de threads, procure evitar o mínimo possível coisas static. A sincronização pode ser no objeto ou nos métodos que gerenciam a lista. Não precisam ser static.

Bom, antes de mais nada, obrigado pelos post !!

O esquema é o seguinte, estou imitando um cruzamento de vias, e simulando semáforo. Eu criei um array list de carro para uma via, uma segunda classe faz a varredura constante desse array list, compara os valores dos objetos (de quando foram lançados na via), e faz comparação com o tempo do semáforo dessa via (true ou false).
Dessa forma, uma classe adiciona objetos (carros) no arrayList (de forma aleatória de tempo), e outra classe faz a varredura, pra saber se o carro foi parado ou se passou pelo semáforo (por comparações de tempo) …
entende ?
eu cheguei a pensar em clonar o arraylist (para efetuar a varredura) … pelomenos leitura eu iria conseguir fazer …

=)
danado né ? hihi … tou lendo deitel aki e tá danado … hihih
obrigado por antecipação viu galera … espero poder ajudar tb … =)

Ok Vini, acho q chegamos na mesma … "clonar " o arry pra poder efetuar a leitura … correto ?
mas tive pensando, acho q ainda posso ter problemas, pois se no ato da leitura (para clonar), houver alteração no array list ?
acredito mesmo é que a unica solução é sincronizar o acesso por completo ao array …
concorda ?
=)

[quote=Frital]Bom, antes de mais nada, obrigado pelos post !!

O esquema é o seguinte, estou imitando um cruzamento de vias, e simulando semáforo. Eu criei um array list de carro para uma via, uma segunda classe faz a varredura constante desse array list, compara os valores dos objetos (de quando foram lançados na via), e faz comparação com o tempo do semáforo dessa via (true ou false).
Dessa forma, uma classe adiciona objetos (carros) no arrayList (de forma aleatória de tempo), e outra classe faz a varredura, pra saber se o carro foi parado ou se passou pelo semáforo (por comparações de tempo) …
entende ?
eu cheguei a pensar em clonar o arraylist (para efetuar a varredura) … pelomenos leitura eu iria conseguir fazer …
[/quote]

Batata que era de uma Queue que vc precisava. Como falei antes, uma BlockingQueue.

Duas threads, uma produz objetos (carros) na queue (estrada antes do semoforo), e outra consome ( semaforo). O semaforo consome conforme alguma regra e depois coloca em outra queue ( a estrada depois do semaforo)
Se cruzarmos dois semaforos com duas queues origem diferentes e a mesma queue final podemos ver que os carros se alternam entre as duas estradas conforme a regra do semaforo

não ha nencessidade nenhuma de iterar, nem editar a queue.

Pois é Sergio … tava chegando nessa BlockingQueue … lendo … hihi

ps.: mas só pra vc saber … a coisa funciona bem mais simples …
eu lanço um carro na via, com um tempo “estimado” pra ele passar pelo cruzamento … a verificação é simples … se durante a verificação (leitura), o tempo de vida dele for menor que o estimado, e o sinal estiver em vermelho, ele será considerado um carro que foi parado pelo semáforo …
=)

ok ?
vou ler um bocado sobre queue … e quando aparecere dúvidas crueis dinovo eu posto … =) muito obrigado novamente galera …
andei percorrendo as dúvidas, mas até no java basico as perguntas tão dificeis pra mim … (as que eu sei, já tão respondidas) … hhihih
=)
mas vou ficar de olho …
=)

Só para acrescentar aqui uma opinião.
Normalmente simulações não usam threads - o que você deve normalmente fazer é dividir a simulação em instantes discretos de tempo (0.1s, 0.2s, 0.3s etc.) e nesses instantes, calcular todas as coisas que ocorrem (nesse caso, os carros que trafegam devem mudar de posição ou pararem no farol.)
Até porque é muito difícil debugar uma simulação se usar threads.
O que se usa é um gerador de números aleatórios único cuja semente pode ser mantida constante (para poder repetir e debugar uma simulação) ou então deixar a cargo do relógio do computador (para termos simulações diferentes).

Certíssimo entanglement. De fato consigo pegar essa “semente” da geração aleatória (na verdade, jogar a “razão” de cálculo em uma variável para reutilização). Dessa maneira conseguirei realizar as devidas análises … =)
não consegui “bolar” de forma diferente a não ser com multithread … mas acredito que deva haver uma forma melhor …
=)

obrigado novamente …
vou postar quando conseguir resolver …
=))

[quote=Frital]Ok Vini, acho q chegamos na mesma … "clonar " o arry pra poder efetuar a leitura … correto ?
mas tive pensando, acho q ainda posso ter problemas, pois se no ato da leitura (para clonar), houver alteração no array list ?
acredito mesmo é que a unica solução é sincronizar o acesso por completo ao array …
concorda ?
=)[/quote]

A classe que indiquei faz dá essas garantias automaticamente.

Algum motivo especial para não usar filas, ou mesmo para usar threads?

Bom pessoal, conforme prometido, posto que consegui resolver meu problema:
como eu tinha várias classes (em threads diferentes) acessando o mesmo arraylist, eu sincronizei o acesso aos métodos, resolvendo desta forma meu problemas
Bom, se alguém precisar de ajuda neste sentido, espero poder conseguir contribuir. Muito obrigado a todos que postaram.

[quote=rafadelnero]Use a classe Vector, ela tem seus métodos synchronized que evitam concorrência.[/quote]Vector não resolve esse problema pq o Iterator não é sincronizado.

A exceção que ocorre é porque você modificou a lista enquanto iterava ela. Ou seja, você NÃO pode fazer ambos ao mesmo tempo, não importa se for em threads diferentes ou na mesma thread.

Quando usando threads diferentes, antes eu utilizava monitores simples no formato:

class MyClass { private Object mutex = new Object(); void myMethod () { synchronized (mutex) { //faça algo que deva estar sincronizado } } }
Mas atualmente, nesses casos acabo preferindo usar ReentrantReadWriteLock
Ele possui dois métodos: readLock() e writeLock() que fornecem objetos do tipo Lock e possuem os métodos lock() e unlock().

Numa situação de leitura, use o readLock e numa situação de escrita, use o writeLock.

Agora, no caso de inserção durante a iteração (mesma thread):

void meuMetodo(List<Object> lista) { for (Iterator<Object> iterator = lista.iterator(); iterator.hasNext();) { Object object = iterator.next(); if(/* algo que retorna boolean true */) { lista.add(new Object()); //Aqui gera problemas. } else { lista.remove(object); //Aqui deve-se trocar por iterator.remove(); } } }Neste caso, deve-se mudar a forma de iterar a lista ou na forma de tratar novos objetos na lista (itera e controla os objetos a serem inseridos posteriormente).

Não acho apropriado o uso de synchronizedList pois o Iterator não é sincronizado e você é obrigado, de qualquer maneira, a colocá-lo num bloco síncrono. Isso é explicitado no próprio JavaDoc: http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#synchronizedList(java.util.List)

[quote=JavaDoc]Returns a synchronized (thread-safe) list backed by the specified list. In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.
It is imperative that the user manually synchronize on the returned list when iterating over it:

  List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

Failure to follow this advice may result in non-deterministic behavior.
The returned list will be serializable if the specified list is serializable.[/quote]