2010-12-04 4 views
4

Comment effectuer une opération sécurisée en cas d'opération sur une carte de hachage concomitante? (Même chose comme putIfAbsent)Java ConcurrentHashMap atomique get si présent

exemple Bad, pas très thread-safe (cocher agir alors la situation):

ConcurrentMap<String, SomeObject> concMap = new ... 

//... many putIfAbsent and remove operations 

public boolean setOption(String id, Object option){ 
    SomeObject obj = concMap.get(id); 

    if (obj != null){ 
     //what if this key has been removed from the map? 
     obj.setOption(option); 
     return true; 
    } 

    // in the meantime a putIfAbsent may have been called on the map and then this 
    //setOption call is no longer correct 

    return false; 
} 

Un autre mauvais exemple serait:

public boolean setOption(String id, Object option){ 
     if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
      return true; 
     } 
     return false; 
    } 

La chose souhaitable est ici pas goulot d'étranglement l'ajout, supprimer et obtenir des opérations en les synchronisant.

Merci

+0

Je ne comprends pas votre commentaire sur "cet appel setOption" - à quelle méthode setOption faites-vous référence? Vous en avez deux dans votre code. –

+0

C'est juste une méthode de jeu générique sur un objet générique qui est stocké dans la carte. – cdmihai

+0

Votre deuxième exemple serait défectueux si les paires clé-valeur pouvaient être retirées de la carte. Cela pourrait arriver entre le test contient et le get. –

Répondre

1

Ce que vous semblez essayer de faire est de verrouiller une clé sur plusieurs opérations. Seule chaque opération est atomique. Ce n'est pas un moyen simple de verrouiller une clé, seulement pour verrouiller la carte. Toutefois, dans le cas "que se passe-t-il si je supprime une clé", tout ce que vous pouvez faire est de retarder l'opération de suppression jusqu'à ce que setOption soit appelée. Le résultat devrait être le même.

Vous semblez essayer de résoudre un problème qui n'a pas besoin d'être résolu. Vous n'avez pas expliqué pourquoi appeler setOption après la suppression d'une clé ou la suppression de la clé est incorrect.

+0

Ah, merci pour votre réponse, j'ai compris mon problème, c'est juste un cas modifié du problème des écrivains lecteurs. Supposons que j'ai un ensemble d'opérations set1 = {op1, op2, op3 ...} qui peuvent être exécutées en parallèle, mais chaque fois qu'une opération dans set2 = {opr1, opr2, ..} est exécutée les opérations éventuelles de set1 et le reste des opérations de set2 doit attendre. – cdmihai

+0

@PeterLawrey J'ai aussi une question similaire sur CHM [ici] (http://stackoverflow.com/questions/41135998/how-to-atomically-update-the-value-of-concurrentmap-in-multithreaded-application). Je suis en train d'essayer de mettre à jour la valeur de la commande simulthashmap en mode thread safe et atomique. –

2

Ne pas utiliser containsKey/get, il suffit d'appeler get. Si cette méthode renvoie null alors la clé n'était pas présente sinon la clé était présente, et vous avez obtenu la valeur à laquelle elle était mappée au moment du get.

De la documentation:

Renvoie la valeur à laquelle la clé spécifiée est mappée ou null si cette carte ne contient pas de correspondance pour la clé.

Voici comment votre deuxième exemple devrait ressembler à:

public boolean setOption(String id, Object option) { 

    SomeObject opt = concMap.get(id); 
    if (opt == null) 
     return false; 

    opt.setOption(option); 
    return true; 
} 
+0

Mais que se passe-t-il si le get retourne null car au moment de l'appel cette clé n'était pas présente, mais entre l'appel get et le test sur son retour cette clé a été ajoutée à la map (d'un autre thread)? – cdmihai

+3

Est-ce que cela serait conceptuellement différent d'un autre thread ajoutant la paire clé-valeur APRÈS le test nul? –

+0

Que faire si la valeur est un booléen? Vous ne pouvez pas comparer une valeur booléenne à une valeur nulle, vous devez donc probablement utiliser la propriété containsKey. –

6

La méthode get() sur un ConcurrentHashMap est atomique. Puisque cette carte n'autorise pas les valeurs nulles, get() implémente "get if present": Si le résultat est null, la clé n'était pas présente.

+0

Mais que se passe-t-il si le get est retourné nul parce que la clé n'était pas présente au moment de l'appel, mais entre l'appel get et le test sur sa valeur de retour, cette clé a été ajoutée à la map? – cdmihai

+0

Le get renvoie une valeur nulle si cette clé n'est pas présente. Mais si c'était ajouté entre le get et le test nul? – cdmihai

+0

La valeur retournée ne changera pas simplement parce qu'elle a changé dans la carte. –

0

Si vous devez effectuer plusieurs opérations sur une seule touche à ConcurrentMap, vous pouvez utiliser striping verrouillage technique pour réduire les conflits, voici un exemple avec le cadre Goyave:

private Striped<Lock> lock; 
    public boolean setOption(String id, Object option) { 
     try { 
     Lock lock = concMap.get(id); 
     lock.lock(); 
      if (concMap.contains(id)){ 
      concMap.get(id).setOption(option); 
     return true; 
    } 
    return false; 
     } finally { 
     lock.unlock(); 
     } 
    } 

Ou, depuis Java 8 : ConcurrentMap.compute est une nouvelle méthode atomique, voir comment il se fait sur une clé:

concMap.compute(keyId, (key, value) -> { 
    dosmth; ... return key; }); 

ps Les variantes possibles sont avec ConcurrentMap.computeIfPresent(), etc.