2010-03-26 4 views
9

J'ai quelques questions sur l'assigment de Java.Problèmes d'attribution Java - Est-ce atomique?

  • Cordes

J'ai une classe:

public class Test { 
private String s; 

public synchronized void setS(String str){ 
    s = s + " - " + str; 
} 

public String getS(){ 
    return s; 
} 
} 

J'utilise "synchronisé" dans mon setter, et en évitant dans mon getter, parce que dans mon application, il y a des tonnes de données, et très peu de paramètres. Les paramètres doivent être synchronisés pour éviter les incohérences. Ma question est: est d'obtenir et de définir une variable atomique? Je veux dire, dans un environnement multithread, Thread1 est sur le point de définir des variables, tandis que Thread2 est sur le point d'obtenir "s". Est-il possible que la méthode getter puisse avoir quelque chose de différent de l'ancienne valeur de s ou de la nouvelle valeur de s (supposons que nous ayons seulement deux threads)? Dans mon application, il n'est pas un problème pour obtenir la nouvelle valeur, et ce n'est pas un problème pour obtenir l'ancien. Mais pourrais-je avoir autre chose?

  • Et qu'en est-il de HashMap?

considérant ceci:

public class Test { 
     private Map<Integer, String> map = Collections.synchronizedMap(new HashMap<Integer, String>()); 

     public synchronized void setMapElement(Integer key, String value){ 
     map.put(key, value); 
     } 

     public String getValue(Integer key){ 
     return map.get(key); 
     } 
} 

met et obtenir atomique? Comment HashMap gère-t-il l'insertion d'un élément? Est-ce qu'il supprime d'abord l'ancienne valeur et met le maintenant? Pourrais-je avoir autre chose que l'ancienne valeur ou la nouvelle valeur?

Merci d'avance!

+0

+1 pour spécifier que les lecteurs n'ont pas besoin de voir la dernière valeur absolue. Bien sûr, cela devrait certainement être documenté dans le code de production. –

Répondre

6

Dans le premier cas, String s'avère être sûr pour une publication non sécurisée (dans le "nouveau" Java Memory Model (JMM)), donc c'est correct.

N'étant pas volatile il y a théoriquement quelque problème à ne pas avoir une valeur à jour, mais alors la signification de mise à jour n'est pas claire. Vous pouvez remplacer le verrou par une boucle CAS (compare-sand-swap), mais cela ne vous donnera probablement pas beaucoup de gain de performance, que le verrou soit susceptible d'être contesté ou non.

Dans le cas de HashMap, une carte non synchronisée n'est pas sûre à lire s'il y a une autre écriture de thread, même un seul thread d'écriture. En fait, cela s'est avéré conduire à des boucles infinies sur les systèmes de production exécutant des logiciels populaires. Le code de la question utilise deux verrous pour la carte, ce qui est supérieur (bien que vous ayez besoin d'un maintien explicite du même verrou si vous utilisez un itérateur). Le fait de ne pas être final empêche la classe conteneur d'être sûre pour une publication non sécurisée. Si map était volatile et vous avez créé une nouvelle carte pour chaque put, alors cela pourrait être fait pour être sûr sans synchronisation sur le get.

+0

J'utilise Collections.synchronizedMap. Est-ce aussi dangereux? Je ne fais de modification structurelle que chez mes setters. Donc, je synchronise ces méthodes, mais mes getters obtiennent juste les données. Dois-je les synchroniser aussi? J'ai peur que pendant que je mets une valeur dans le HashMap un autre thread aimerait obtenir la valeur, et il obtient autre chose que l'ancienne valeur ou la nouvelle. – Bob

+0

'Collections.synchronizedMap' ajoute la synchronisation, comme son nom l'indique, donc vous n'avez pas besoin de le faire. Vous pourriez avoir une meilleure idée de ce qu'il fait si vous insérez son code dans le vôtre et utilisez le 'HashMap' directement. –

6

Plutôt que d'encapsuler votre HashMap dans quelque chose pour le synchroniser, pensez plutôt à utiliser un java.util.concurrency.ConcurrentHashMap.

Ceci est une version mise à jour de HashMap qui garantit que les "Retrievals reflètent les résultats des dernières opérations de mise à jour terminées dès leur apparition.".

3

Les réponses précédentes sont correctes en indiquant qu'avec les nouvelles machines JVM (1.5+), la version String est sans danger, en ce qui concerne la corruption de données. Et vous semblez être conscient de l'accès non synchronisé à la baisse; celui des changements n'étant pas nécessairement visible via les getters.

Cependant: question plus utile est la suivante: y a-t-il une raison de synchronisation ici? Si c'est juste pour être intéressé à savoir cela, c'est bien. Mais pour le code réel, la règle générale des lectures et écritures est que s'il y a les deux, les deux devraient être synchronisés. Donc, bien que vous puissiez dans ce cas omettre la synchronisation (ce qui signifie potentiellement que les threads ne voient pas les changements que font les autres threads), il semble y avoir peu d'avantages à le faire.

+0

Mon application est un "moteur de blog" comme application. Je stocke le premier X (disons 10) des entrées nouvellement créées dans une variable. Ce n'est pas un problème que les utilisateurs ne reçoivent pas la version à jour des entrys, car ils pourraient actualiser la page s'ils le voulaient. Je fais environ 20 entrys en une journée (setter), mais j'ai des centaines de milliers de visiteurs par jour. Il n'est pas efficace (je pense) de synchroniser les méthodes de getter (j'ai beaucoup de méthodes getter comme ci-dessus). (Non, je n'ai pas encore reçu des centaines de milliers de visiteurs par jour, mais je veux que mon application soit capable de gérer cela) – Bob

+5

Notez que ce n'est pas seulement une question d'actualisation de la page par l'utilisateur. Dans le modèle de mémoire Java, s'il n'y a pas de synchronisation entre le thread setter et le thread getter, il est possible que le thread getter n'obtiendra * jamais * la valeur mise à jour. Cela ressemble à une chose de type optimisation prématurée pour moi. Faites-le d'abord correct. Si vous constatez plus tard que la synchronisation limite réellement votre débit, vous pouvez alors rechercher de meilleures façons de le gérer. –

+0

"il est possible que le thread getter n'obtienne jamais la valeur mise à jour." Comment venir? Je ne comprends pas. "35 sec 300 millisec" J'obtiens la valeur, c'est l'ancienne. "35 sec 500 millisec" J'ai mis la valeur, que "50 sec xxx millisec" J'essaie d'obtenir le nouveau? Comment est-il possible que je n'obtiendrai jamais la nouvelle valeur? – Bob

1

Dans un environnement multithread vous devrez synchroniser le getter afin de vous assurer que le client voit la valeur la plus à jour de s.

+0

rendant la variable "volatile" serait tout aussi efficace et ne nécessite pas le surcoût de synchronisation –

+2

'volatile' a son propre overhead de synchronisation intégré. – Bombe

2

Peut-être qu'un Read Write Lock pourrait résoudre votre problème?

Jetez un oeil à sa documentation:

Un verrou en lecture-écriture permet un plus grand niveau de l'accès aux données concurrency partagées que celle permise par un verrou d'exclusion mutuelle. Il exploite le fait que si un seul thread à la fois (un thread d'écriture) peut modifier les données partagées, dans un grand nombre de cas, un nombre quelconque de threads peut lire simultanément les données (donc les threads de lecture). En théorie, l'augmentation de la concurrence permise par l'utilisation d'un verrou en lecture-écriture conduira à des améliorations de performance par rapport à l'utilisation d'un verrou d'exclusion mutuelle.

+0

+1 mais je serais surpris si les collections synchronisées ne l'utilisent pas déjà. – HRJ