2015-06-28 9 views
5

Sto facendo un gioco e ho un ConcurrentHashMap che contiene tutti i giocatori attualmente loggati. Ho una sequenza AutoSaver che scorre attraverso HashMap e salva tutto il giocatore 1 per 1. Quando non ci sono molti giocatori questo va bene in quanto non ci vuole troppo tempo per iterare ma può rallentare un po 'quando ci sono molti giocatori loggati. Ho letto usando java stream e parallel, possiamo accelerare l'elaborazione delle raccolte, quindi ho provato a modificare il mio ciclo esistente per utilizzare ora lo streaming e parallelo.Cambiare il ciclo foreach esistente su ConcurrentHashMap per utilizzare Lambda per sfruttare l'elaborazione parallela

La mia domanda è, la mia implementazione è corretta? C'è un modo migliore per farlo? È sicuro il thread ora?

Ecco l'attuale implementazione

for(Player player : ActiveConnections.getAllConnectedPlayers().values(){ 
    if(player != null) 
     saveManager.savePlayer(player, true); 
} 

Qui è la mia implementazione utilizzando flusso e parallela

ActiveConnections.getAllConnectedPlayers().values() 
    .stream() 
    .parallel() 
    .filter((x) -> x != null) 
    .forEach((x) -> saveManager.savePlayer(x, true)); 

EDIT Ecco il mio risparmio implementazione direttore

public class SaveManager { 

    private MySqlManager sqlManager; 

    public SaveManager(){ 
     sqlManager = MySqlManager.getInstance(); 
    } 

    public void savePlayer(Player player, boolean autoSave){ 
     //Saves the player 
    } 

A guadagno Ho appena iniziato a usare lambda quindi per favore fatemi sapere se c'è qualcosa di sbagliato.

+0

Questo sembra giusto per me ... Questa domanda potrebbe essere più adatta a [Code Review] (http://codereview.stackexchange.com/), però. –

+0

Non avevo idea che ci fosse qualcosa come la revisione del codice sullo stack overflow. :) Lo sposterò se è necessario. – Sneh

+0

La documentazione di 'ConcurrentHashMap' afferma chiaramente che" [...] Come Hashtable ma a differenza di HashMap, questa classe non consente l'utilizzo di null come chiave o valore. "- quindi perché il test per null? – fge

risposta

1

È thread-safe se savePlayer è thread save. trasformare uno stream in uno parallelo non lo rende thread-safe, rende l'algoritmo in grado di essere parallelizzato.

Tuttavia, se il tuo savePlayer salva elementi nel database, non c'è modo di parallelizzare la parte di salvataggio del giocatore che è ciò che desideri. Il che significa che vedrete alcun beneficio dall'utilizzo di un flusso parallelo, perché quando un thread cambia il contenuto del DB, possono accadere due cose:

  • il secondo thread che vuole salvare un altro giocatore, attende per la prima filo per finire. In tal caso, non vi è alcun vantaggio nell'utilizzare flussi paralleli perché i thread devono ancora attendere l'uno con l'altro.

  • il secondo thread tenta di modificare i dati DB allo stesso tempo ha il primo thread che può portare a dati incoerenti sul database. Supponendo che il tuo codice supporti più di una connessione attiva al database.

In sintesi, è necessario utilizzare il flusso parallelo quando l'algoritmo che si desidera eseguire è parallelizzabile. Internamente, parallelStream() divide il flusso in sotto-flussi ed esegue l'algoritmo per ciascun elemento su ciascuno dei sub-flussi (simultaneamente), alla fine il risultato di ogni flusso secondario viene combinato utilizzando lo stesso algoritmo.

Un esempio dal libro "Java 8 in azione":

public static long parallelSum(long n){ 
    return Stream.iterate(1L, i -> i + 1) // generate a stream of long values, starting at 1 
       .limit(n) // limit the stream to n items 
       .parallel() 
       .reduce(0L, Long::sum); // this is what we want to execute concurrently, and in the end the result of each sub-stream will be combined using this sum 
} 

Per ulteriori informazioni consultare il capitolo 7 del libro.

+0

Ho modificato la mia domanda e ho aggiunto la classe SaveManager. Non sono sicuro di come posso finire con dati coerenti sul database. – Sneh

+1

Stai deviando dal punto. Il punto è che se savePlayer è thread-safe, allora il tuo codice lambda è corretto e anche thread-safe. La coerenza dei dati del DB può essere garantita con le transazioni atomiche, utilizzando meccanismi di commit e rollback. L'incoerenza dei dati è un problema se il thread principale può cambiare i dati del giocatore nello stesso momento in cui il thread AutoSaver sta salvando i giocatori. Ma non so se questo può accadere, quindi ho pensato che potesse. – pedromss

+0

Sì, questo può succedere. Il mio thread principale può cambiare il valore dei giocatori, diciamo la sua posizione su una mappa di gioco (se il giocatore è in movimento). Quindi penso di aver capito il tuo punto. Grazie – Sneh