2009-08-23 10 views
3

J'essaie d'utiliser ThreadLocal pour fournir une sécurité de thread aux classes préexistantes non thread-safe, mais rencontrant des problèmes. Il semble qu'il n'y ait pas d'isolation en cours - que les threads partagent encore le statique, au lieu d'être local à chaque thread.Utilisation de ThreadLocal avec des classes existantes qui contiennent des membres statiques

Je crois que mon utilisation est presque exactement parallèle à l'exemple de localisation de SimpleDateFormatter décrit dans this StackOverflow question, mais cela ne fonctionne pas comme je l'espère. Ce que j'espère, c'est que quelqu'un là-bas qui a utilisé cela va souligner l'erreur éhontée je dois faire ... donc je suppose que ma question est: pouvez-vous repérer ce que je fais mal ici?

Voilà ma classe simple:

public class SimpleClassWithStaticMembers { 
    private static String theStaticString = 
     "StaticStringInClassWithStaticMember"; 
    public void setTheStaticString (String val) { 
     SimpleClassWithStaticMembers.theStaticString = val; 
    } 
    public String getTheStaticString() { 
     return SimpleClassWithStaticMembers.theStaticString; 
    } 
} 

Et ceci est la classe de fil qui crée des instances ThreadLocal de SimpleClassWithStaticMembers:

public class SimpleTesterThread extends Thread { 
    private void showMsg (String msg) { 
     System.out.println (msg); 
     System.out.flush(); 
    } 
    public SimpleTesterThread (String threadId) { 
     super(threadId); 
    } 
    public void run() { 
     try { Thread.sleep(2000); } catch (InterruptedException ex) { } 
     ThreadLocal<SimpleClassWithStaticMembers> localizedClass = 
      new ThreadLocal<SimpleClassWithStaticMembers>(); 
     localizedClass.set(new SimpleClassWithStaticMembers()); 
     // repeating here to be sure we overlap all with all 
     for (int ii=0; ii < 3; ii++) { 
      localizedClass.get().setTheStaticString ("Setby_" + this.getName()); 
      try { Thread.sleep(2000); } catch (InterruptedException ex) { } 
      showMsg("   Thread [" + this.getName() + "] - " 
       + localizedClass.get().getTheStaticString() + "'."); 
     } 
     showMsg ("Thread [" + this.getName() 
      + "] complete. Our 'threadlocal' string is now - " 
      + localizedClass.get().getTheStaticString() + "'."); 
     localizedClass.remove(); 
    } 
} 

Lorsque dix cas de SimpleTesterThread sont créés (en leur donnant des noms de fils distincts comme "AAAAAAAAAA", "BBBBBBBBB", etc), puis démarré, la sortie montre clairement qu'ils partagent des instances. Sortie du journal comprend:

 
... 
      Thread [JJJJJJJJJJ] - Setby_CCCCCCCCCC'. 
      Thread [DDDDDDDDDD] - Setby_JJJJJJJJJJ'. 
      Thread [IIIIIIIIII] - Setby_DDDDDDDDDD'. 
      Thread [GGGGGGGGGG] - Setby_IIIIIIIIII'. 
      Thread [EEEEEEEEEE] - Setby_GGGGGGGGGG'. 
Thread [EEEEEEEEEE] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [HHHHHHHHHH] - Setby_GGGGGGGGGG'. 
      Thread [BBBBBBBBBB] - Setby_GGGGGGGGGG'. 
      Thread [FFFFFFFFFF] - Setby_GGGGGGGGGG'. 
Thread [FFFFFFFFFF] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
... 
      Thread [JJJJJJJJJJ] - Setby_GGGGGGGGGG'. 
Thread [JJJJJJJJJJ] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [HHHHHHHHHH] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [GGGGGGGGGG] - Setby_GGGGGGGGGG'. 
Thread [GGGGGGGGGG] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [IIIIIIIIII] - Setby_GGGGGGGGGG'. 
      Thread [CCCCCCCCCC] - Setby_GGGGGGGGGG'. 
Thread [CCCCCCCCCC] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [AAAAAAAAAA] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
      Thread [DDDDDDDDDD] - Setby_GGGGGGGGGG'. 
Thread [DDDDDDDDDD] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
Thread [IIIIIIIIII] complete. Our 'threadlocal' string is now - Setby_GGGGGGGGGG'. 
============== all threads complete. 

Je n'ai pas inclus la classe qui crée, démarre, et rejoint les discussions - si ce Feutres être utile, je vais modifier volontiers à ajouter.

+0

Je me demande si le problème est que vous "démarrez" les threads en appelant la méthode 'run()' au lieu de la méthode 'start()' ... –

+0

Merci pour la suggestion, Stephen.Les threads sont démarrés via la méthode start(), mais cela pourrait certainement avoir un effet. – CPerkins

Répondre

5

Avoir des instances séparées n'aide pas, comme toutes les instances les mêmes champs statiques. Les statistiques statiques sont mauvaises.

Si vous ne pouvez vraiment pas modifier la classe, vous pourriez plutôt vouloir utiliser un verrou pour que chaque client puisse utiliser les champs statiques un à la fois. Si vous voulez avoir différentes instances des champs statiques, alors vous aurez probablement besoin de jouer avec les chargeurs de classe (l'autre solution évidente est de réécrire le bytecode, ce qui est encore moins agréable).

+0

Je sais cela à propos de la statique en dehors de threadlocal, mais j'ai pensé à partir des discussions ici et ailleurs que threadlocal donnait des copies spécifiques de threads statiques - les transformant en un par thread, par opposition à un par classe. Pas vrai? – CPerkins

+0

'ThreadLocal' donne à chaque thread la possibilité d'avoir une référence différente. Ça ne fait rien de magique. Vous pouvez même définir la même référence dans différents threads et vous retrouver exactement avec le même objet. –

+0

C'est décevant. Mais merci. – CPerkins

2

La classe ThreadLocal ne modifie pas le comportement du modificateur de champ statique. Les champs statiques continueront à avoir une incarnation sur plusieurs instances de la classe, car ils sont créés et initialisés lorsque la classe est initialisée.

Pour mieux comprendre ce problème, les membres ThreadLocal sont gérés en interne à l'aide d'une mappe qui existe par thread. Les appels get() et set() fonctionnent sur cette carte. Il n'y a pas de 'magie' dans ThreadLocal, par lequel un membre statique perd sa propriété d'avoir une seule incarnation. Les membres statiques, lorsqu'ils sont définis comme une variable ThreadLocal, sont simplement ajoutés à la carte pour référence dans une autre section du code qui sera exécutée par le thread. Cela n'empêche pas un deuxième thread d'obtenir une référence au membre statique et d'effectuer des opérations sur le champ membre.

C'est pour cette raison que les déclarations de Tom Hawtin doivent être prises au sérieux - les statistiques mutables ne constituent tout simplement pas un bon design. PS: En jetant un coup d'œil sur l'implémentation des classes Thread, ThreadLocal et ThreadLocal.ThreadLocalMap, il serait utile d'effacer les idées fausses sur le comportement des objets ThreadLocal.

+0

Merci, je vais le faire. – CPerkins

Questions connexes