2015-07-08 34 views
6

ho la seguente classe:Java Singleton sincronizzazione per il multi-thread utilizzando HashMap

public class AggregationController { 


    private HashMap<String, TreeMap<Integer, String>> messages; 
    private HashMap<String, Integer> counters; 
    Boolean buildAggregateReply; 
    private boolean isAggregationStarted; 

    private static HashMap<String, AggregationController> instances = new HashMap<String, AggregationController>(); 

    private AggregationController() throws MbException{ 
     messages = new HashMap<String, TreeMap<Integer,String>>(); 
     counters = new HashMap<String, Integer>(); 
     buildAggregateReply = true; 
     isAggregationStarted = false; 
    } 

    public static synchronized AggregationController getInstance(String id) throws MbException{ 
     if(instances.get(id) == null) 
      instances.put(id, new AggregationController()); 
     return instances.get(id); 
    } 

ho pensato che sarebbe stato sufficiente per evitare l'accesso simultaneo, ma ho ottenuto questo errore:

HashMap.java 
checkConcurrentMod 
java.util.HashMap$AbstractMapIterator 
java.util.ConcurrentModificationException 
Unhandled exception in plugin method 
java.util.ConcurrentModificationException 

I ha 10 thread che usano questa classe e lancia questo errore circa una volta ogni 100.000 chiamate.

Che cosa è sbagliato con questo singleton?

+0

completa analisi dello stack con il codice di cui si vede questa eccezione si verificano? – SMA

+0

Sincronizzate le chiamate sulle mappe all'interno della vostra classe? Forse questi accessi potrebbero causare problemi? – red13

+0

Ho trovato il problema. Una delle funzioni (non scritta qui) stava tentando di accedere allo stesso oggetto rispetto all'istanza get. Queste 2 funzioni erano sincronizzate singolarmente, ma potevano essere chiamate nello stesso momento Per risolvere il problema, l'ho modificato in una ConcurrentHashMap e ho anche migliorato la sincronizzazione utilizzando il blocco con doppio controllo. Ora eseguirò i test tutta la notte e ti faccio sapere se il problema è veramente risolto –

risposta

1

Il problema è semplicemente che HashMaps non sono thread-safe come puoi leggere nei documenti collegati.

Provare a cambiarli in ConcurrentHashMaps.

A parte questo, è necessario modificare l'implementazione di singleton per gestire meglio il multi threading. La pagina di Wikipedia su Double-checked locking fornisce molti buoni esempi.

p.s .: Invece di dichiarare le variabili come HashMaps, dovresti semplicemente dichiararle come Maps. In questo modo è possibile modificare l'implementazione specifica molto facilmente senza dover rifattorizzare nulla. Questo è chiamato Programming to interfaces.

+1

Sembra che abbia risolto il mio problema, farò un test di massa questa notte per essere sicuro. Grazie mille per tutti i suggerimenti –

1

Credo che il problema è con HashMap, utilizzare Concurrent HashMap

Ma il punto che voglio fare è la funzione getInstnace() non è ben scritto.

 public static synchronized AggregationController getInstance(String id) throws MbException{ 
    if(instances.get(id) == null) 
     instances.put(id, new AggregationController()); 
    return instances.get(id); 
} 

Si utilizza synchronized su tutto il metodo. Anche se la tua istanza è stata creata, solo una discussione sarà in grado di entrare nel metodo getInstance, che rallenta le prestazioni del tuo programma. Si dovrebbe fare come this:

 public static AggregationController getInstance() { 
      if (instance == null) { 
      synchronized (AggregationController.class) { 
      if (instance == null) { 
       instance = new AggregationController(); 
      } 
      } 
     } 

     return instance; 
    } 
+0

L'espressione [Bloccaggio a doppio controllo] (https://en.wikipedia.org/wiki/Double-checked_locking) da sola non è una soluzione per questo problema. Anche la variabile deve essere volatile. – mhlz

+0

Grazie per l'aiuto –