2014-11-04 17 views
5

Sto cercando di aggiornare del codice per utilizzare le espressioni lambda, ma ho qualche problema a salvaguardare la sicurezza dei thread.Protezione della filettatura con lambda

Ho più thread in esecuzione che alla fine chiamano il seguente callback, che ha un metodo synchronized che aggiunge alcuni risultati a un LinkedList.

final List<Document> mappedDocs = new LinkedList<>(); 
final MapCallback<Integer, Document> mapCallback = new MapCallback<Integer, Document>() { 
    @Override 
    public synchronized void done(int file, List<Document> results) { 
     mappedDocs.addAll(results); 
    } 
}; 

Tuttavia quando ho convertirla in un'espressione lambda perdo la parola synchronized e io non sono del tutto sicuro di come tornare indietro. Ora sto ricevendo una NullPointerException ogni volta che eseguo il mio codice.

final MapCallback<Integer, Document> mapCallback = (int file, List<Document> results) -> mappedDocs.addAll(results); 

Come posso rendere sicuro questo thread?

+1

non puoi semplicemente non usare un lambda? – jtahlborn

+0

@jtahlborn posso. È solo il mio IDE che mi sta dicendo che posso sostituirlo con un lambda, quindi ho pensato di provare. – karoma

+0

ho intenzione di andare con [no] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27) (vedi nessun riferimento a "sincronizzato" lì dentro). – jtahlborn

risposta

7

Si può sincronizzarsi su un monitor diverso, ad es .:

final MapCallback<Integer, Document> mapCallback = (int file, List<Document> results) -> { 
    synchronized(mappedDocs) { 
    mappedDocs.addAll(results); 
    } 
}; 

Oppure utilizzare alternativamente una struttura sicura filo, ad esempio un CopyOnWriteArrayList o un BlockingQueue.

3

Consiglio vivamente di creare mappedDocs una struttura dati thread-safe (come quella da java.util.concurrent) o forse un wrapper sincronizzato creato utilizzando Collections.synchronizedList.

Penso che tu sia fortunato che la sincronizzazione funzioni utilizzando la classe interna anonima. Ciò funziona perché esiste esattamente un'istanza di esso e non esiste alcun altro codice che muta mappedDocs.

(In realtà si potrebbe avere un problema di visibilità memoria, anche come stanno le cose. Se altri thread chiamano MapCallback per aggiungere elementi, cosa altro ha bisogno di sincronizzare il mappedDocs dopo la sua costruzione e prima di leggere gli elementi aggiunti.)

La radice del problema è che una classe interna anonima usata in questo modo è un po 'come una funzione, ma poiché la creazione di un nuovo oggetto si manifesta, è allettante fare cose come la sincronizzazione su di esso. Ma questo è abbastanza fragile. Se questo è stato rifatto in modo da creare più istanze dell'AIC (ad esempio, per elaborare documenti da più fonti), o se vengono creati AIC diversi (ad esempio, per cancellare documenti dall'elenco se hanno bisogno di rielaborazione), sincronizzarsi su singole istanze AIC sarebbe completamente rotto

La conversione di mappedDocs in una struttura dati o wrapper thread-safe si occupa del problema di visibilità della memoria e del problema di accesso simultaneo. Ciò consente di utilizzare il semplice modulo lambda e consente di introdurre nuove operazioni su mappedDocs indipendentemente da quali thread sono operativi su di esso.