Precisa de sincronização de prova de ArrayList em um ambiente multi-threaded

Eu estive nisso por uma semana agora fazendo minha pesquisa sobre como sincronizar corretamente uma ArrayList.

Minha principal questão em poucas palavras é que eu tenho uma ArrayList "master" de objetos. Threads diferentes podem entrar e adicionar / definir / remover desta lista. Eu preciso ter certeza de que quando um thread está interagindo com o ArrayList, outro não está mudando isso.

Agora eu li muitos artigos sobre a "melhor" maneira de lidar com isso:

use collections.synchronizedlistuse CopyOnWriteArrayListuse blocos synchronized () em conjunto com collections.synchronizedlistusar Vector (muitas pessoas são contra isso)

Usando blocos sincronizados em cada iteração, adicionar / definir / remover bloco parece ser o que eu quero, mas as pessoas disseram que há muita sobrecarga.

Então eu comecei a brincar com CopyOnWriteArrayList (eu faço muito mais leituras do que escreve para o meu mestre ArrayList). Isso é bom para leitura, mas o que muitos tópicos do fórum não mencionam é que elementos podem ser adicionados, definidos ou removidos do próprio iterador. Por exemplo (uma versão básica, mas imagine em um ambiente multi-thread):

public static void main(String[] args) {

    class TestObject{
        private String s = "";
        public TestObject(String s){
            this.s = s;
        }

        public void setTheString(String s){
            this.s = s;
        }

        public String getTheString(){
            return s;
        }
    }

    CopyOnWriteArrayList<TestObject> list = new CopyOnWriteArrayList<TestObject>();
    list.add(new TestObject("A"));
    list.add(new TestObject("B"));
    list.add(new TestObject("C"));
    list.add(new TestObject("D"));
    list.add(new TestObject("E"));

    ListIterator<TestObject> litr = list.listIterator();

    while(litr.hasNext()){
      TestObject test = litr.next();
      if(test.getTheString().equals("B")){
         litr.set(new TestObject("TEST"));
      }
    }
}

a linha "litr.set (new TestObject (" TEST "));" jogaria um

java.lang.UnsupportedOperationException

E olhando para a documentação do Java, há uma linha específica descrevendo esse comportamento:

"As operações de alteração de elemento nos próprios iteradores (remover, definir e incluir) não são suportadas. Esses métodos executam UnsupportedOperationException."

Então você é forçado a modificar essa lista usando

list.set(litr.previousIndex(), new TestObject("TEST"));

Agora, tecnicamente, isso não deveria apresentar um problema de sincronização? Se outro thread viesse ao mesmo tempo e, digamos, removesse todos os elementos de "list", o iterador não veria isso, iria definir a "lista" em um determinado índice e lançaria uma exceção porque o elemento nesse ponto não existe mais. Eu simplesmente não entendo o ponto de CopyOnWriteArrayList se você não puder adicionar um elemento através do próprio iterador.

Estou perdendo o ponto de usar CopyOnWriteArrayList?

Eu envolvo cada iterador que acaba tendo que adicionar / definir / remover um elemento em um bloco sincronizado?

Este tem de ser um problema comum com multi-threading. Eu teria pensado que alguém teria feito uma aula que pudesse lidar com tudo isso sem se preocupar ...

Agradecemos antecipadamente por dar uma olhada nisso!

questionAnswers(4)

yourAnswerToTheQuestion