2009-07-15 4 views
20

Esistono problemi di concorrenza con una lettura thread da un indice di un array, mentre un altro thread scrive su un altro indice dell'array, purché gli indici siano diversi?java array thread-safety

ad es. (Questo esempio non necessariamente raccomandato per l'uso vero e proprio, ma solo per illustrare il mio punto)

class Test1 
{ 
    static final private int N = 4096; 
    final private int[] x = new int[N]; 
    final private AtomicInteger nwritten = new AtomicInteger(0); 
    // invariant: 
    // all values x[i] where 0 <= i < nwritten.get() are immutable 

    // read() is not synchronized since we want it to be fast 
    int read(int index) { 
     if (index >= nwritten.get()) 
      throw new IllegalArgumentException(); 
     return x[index]; 
    } 
    // write() is synchronized to handle multiple writers 
    // (using compare-and-set techniques to avoid blocking algorithms 
    // is nontrivial) 
    synchronized void write(int x_i) { 
     int index = nwriting.get(); 
     if (index >= N) 
      throw SomeExceptionThatIndicatesArrayIsFull(); 
     x[index] = x_i; 
     // from this point forward, x[index] is fixed in stone 
     nwriting.set(index+1); 
    }  
} 

edit: criticare questo esempio non è la mia domanda, io letteralmente voglio solo sapere se l'accesso array per un indice, in concomitanza a l'accesso a un altro indice, pone problemi di concorrenza, non potrebbe pensare ad un semplice esempio.

risposta

12

Mentre non sarà possibile ottenere uno stato non valido cambiando array come si menziona, si avrà lo stesso problema che si verifica quando due thread stanno visualizzando un intero non volatile senza sincronizzazione (vedere la sezione in Java Tutorial su Memory Consistency Errors). Fondamentalmente, il problema è che Thread 1 può scrivere un valore nello spazio i, ma non c'è alcuna garanzia quando (o se) Thread 2 vedrà il cambiamento.

La classe java.util.concurrent.atomic.AtomicIntegerArray fa ciò che si vuole fare.

+0

grazie ... drat, volevo usare un array byte [] e sembra che non ci sia un tale animale atomico .... Immagino che userò solo i metodi sincronizzati e lo terrò semplice. –

+2

Se hai molte più letture rispetto alle scritture potresti voler guardare java.util.concurrent.locks.ReadWriteLock –

+0

huh, interessante ... –

4

L'esempio ha un sacco di cose che differiscono dalla domanda in prosa.

La risposta a questa domanda è che gli elementi distinti di un array sono accessibili indipendentemente, quindi non è necessario sincronizzare se due thread cambiano elementi diversi.

Tuttavia, il modello di memoria Java non garantisce (che io sappia) che un valore scritto da un thread sarà visibile su un altro thread, a meno che non si sincronizzi l'accesso.

A seconda di cosa si sta veramente cercando di realizzare, è probabile che lo java.util.concurrent abbia già una classe che lo farà per voi. E se così non fosse, ti consiglio comunque di dare un'occhiata al codice sorgente per ConcurrentHashMap, poiché il tuo codice sembra fare la stessa cosa che fa per gestire la tabella hash.

1

Non sono davvero sicuro se la sincronizzazione del solo metodo write, lasciando il metodo read non sincronizzato funzionerebbe. Non proprio quali sono tutte le conseguenze, ma almeno potrebbe portare al metodo read restituendo alcuni valori che sono stati appena sostituiti da write.

1

Sì, poiché un interleaving della cache errato può ancora verificarsi in un ambiente multi-cpu/core. Ci sono diverse opzioni per evitarlo:

  • utilizzare la libreria non sicuri Sun-privato a atomicamente impostare un elemento di un array (o la jsr166y caratteristica aggiunta in Java7
  • Usa AtomicXYZ [] array
  • Usa personalizzato oggetto con un campo di volatili e hanno una serie di quell'oggetto.
  • Utilizzare il ParallelArray di jsr166y addendum al posto nel vostro algoritmo
1

Dal read() non è sincronizzato si potrebbe avere la seguente scen ario:

Thread A enters write() method 
Thread A writes to nwriting = 0; 
Thread B reads from nwriting =0; 
Thread A increments nwriting. nwriting=1 
Thread A exits write(); 

Dal momento che si vuole garantire che i vostri indirizzi delle variabili mai di conflitto, che dire qualcosa di simile (attualizzando temi indice array):

int i; 
synchronized int curr(){ return i; } 
synchronized int next(){ return ++i;} 

int read() { 
     return values[curr()]; 
    } 

void write(int x){ 
    values[next()]=x; 
} 
+0

grazie, ma non la mia domanda e il tuo scenario non può accadere (i passaggi 2 e 3 non si verificherebbero mai) –