2014-10-17 14 views
16

potrzebne struktury ArrayList jak pozwalając tylko następujące operacjewspółbieżne ArrayList

  • get(int index)
  • add(E element)
  • set(int index, E element)
  • iterator()

ze względu na iteracyjnej wykorzystywane w wielu miejsc, używając Collections#synchronizedList wo być podatnym na błędy. Lista może rozwinąć się do kilku tysięcy elementów i wiele się jej używa, więc jestem prawie pewna, że ​​CopyOnWriteArrayList będzie zbyt wolny. Zacznę od tego, aby uniknąć przedwczesnych optymalizacji, ale założę się, że to nie zadziała.

Najczęściej dostępnymi odczytami są jednowątkowe. Pytam więc, jaka jest właściwa struktura danych.


I mimo że owijanie synchronizedList w coś zapewniając zsynchronizowane iterator zrobi, ale nie ze względu na ConcurrentModificationException. Koncentrując się na współbieżnym zachowaniu, oczywiście potrzebuję, aby wszystkie zmiany były widoczne po kolejnych odczytach i iteratorach.

Iterator nie musi wyświetlać spójnej migawki, może aktualizować się lub nie przez set(int index, E element), ponieważ ta operacja zostanie użyta tylko w celu zastąpienia elementu jego zaktualizowaną wersją (zawierającą pewne dodatkowe informacje, które są nieistotne dla użytkownik iteratora). Przedmioty są w pełni niezmienne.


Wyjaśniłem wyraźnie, dlaczego nie zrobiłoby to CopyOnWriteArrayList. ConcurrentLinkedQueue nie wchodzi w grę, ponieważ nie ma dostępu indeksowanego. Potrzebuję tylko kilku operacji, a nie pełnowartościowego ArrayList. Tak więc, chyba że każde pytanie związane z listą równoległą do java o numerze jest duplikatem this question, to nie jest.

+2

Możesz wypróbować Clojure w [ 'PersistentVector'] (https://github.com/clojure/clojure/blob/master/src/jvm/ clojure/lang/PersistentVector.java), jest również kopiowaniem przy zapisie, ale nie naiwnym monolitycznym układem; raczej szerokie i płytkie drzewo tablic. Na końcu kodu źródłowego, do którego link się znajduje, znajdziesz kod testowy. –

+1

@skaffman Czy możesz uprzejmie przeczytać pytanie przed jego zamknięciem? Zobacz moją aktualizację. – maaartinus

+0

@MarkoTopolnik To by działało, ale kod wygląda bardzo dziwnie. Dodaje także trochę narzutów potrzebnych do operacji, na których mi nie zależy. Przy okazji, czy mógłbyś zagłosować na ponowne otwarcie? IMHO mod był nieco zbyt szybki przy czytaniu. – maaartinus

Odpowiedz

4

W twoim przypadku możesz użyć ReadWriteLock, aby uzyskać dostęp do listy kopii, co pozwala wielu wątkom na odczytanie listy. Tylko jeśli jeden wątek wymaga dostępu do zapisu, cały czytnik - wątek musi czekać na zakończenie operacji. Javadoc sprawiają za to jasne:

ReadWriteLock utrzymuje parę stowarzyszonych zamków, jeden dla operacji tylko do odczytu i jeden na piśmie. Blokada odczytu może być przechowywana jednocześnie przez wiele wątków czytnika, o ile nie ma pisarzy . Blokada zapisu jest wyjątkowa.

Oto przykład:

public class ConcurrentArrayList<T> { 

    /** use this to lock for write operations like add/remove */ 
    private final Lock readLock; 
    /** use this to lock for read operations like get/iterator/contains.. */ 
    private final Lock writeLock; 
    /** the underlying list*/ 
    private final List<T> list = new ArrayList(); 

    { 
     ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); 
     readLock = rwLock.readLock(); 
     writeLock = rwLock.writeLock(); 
    } 

    public void add(T e){ 
     writeLock.lock(); 
     try{ 
      list.add(e); 
     }finally{ 
      writeLock.unlock(); 
     } 
    } 

    public void get(int index){ 
     readLock.lock(); 
     try{ 
      list.get(index); 
     }finally{ 
      readLock.unlock(); 
     } 
    } 

    public Iterator<T> iterator(){ 
     readLock.lock(); 
     try { 
      return new ArrayList<T>(list).iterator(); 
        //^ we iterate over an snapshot of our list 
     } finally{ 
      readLock.unlock(); 
     } 
    } 
+0

To dobrze, ale nie mogę użyć iteratora 'ArrayList's, ponieważ spowodowałoby to' ConcurrentModificationException'. Sądzę, że istnieje proste rozwiązanie. – maaartinus

+0

Nie można uniknąć wyjątku'ConcurrentModificationException', jeśli chcesz mieć możliwość dodawania elementów podczas iteracji. Wyobraź sobie następny element podczas iteracji w indeksie 5, ale w tym samym czasie inny wątek dodaje element w indeksie 1, co powinno się teraz wydarzyć? Jedynym wyjściem jest utworzenie immutalbe kopii listy, aby uzyskać iterator niezaliczony lub nadużyć blokadę zapisu, aby zablokować blok pętli iteracji. – Chriss

+0

"* co powinno się teraz stać *" - Dokładnie tak się nie stanie, ponieważ nie będzie takiej operacji. Ale dołączanie lub zastępowanie elementów może, a potem po prostu nie obchodzi mnie to. Uzyskanie starej wartości jest w porządku, a więc otrzymuje nową. – maaartinus