Pourquoi les variables Java ThreadLocal devraient-elles être statiques?

101

Je lisais le JavaDoc pour Threadlocal ici

https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html

et il dit "Les instances ThreadLocal sont généralement des champs statiques privés dans les classes qui souhaitent associer un état à un thread (par exemple, un ID utilisateur ou un ID de transaction)."

Mais ma question est pourquoi ont-ils choisi de le rendre statique (généralement) - cela rend les choses un peu déroutantes d'avoir un état "par thread" mais les champs sont statiques?

kellyfj
la source

Réponses:

131

Parce que s'il s'agissait d'un champ au niveau de l'instance, alors ce serait en fait "Par Thread - Par Instance", pas simplement un "Par Thread" garanti. Ce n'est normalement pas la sémantique que vous recherchez.

Habituellement, il contient quelque chose comme des objets qui sont étendus à une conversation utilisateur, une requête Web, etc. Vous ne voulez pas qu'ils soient également sous-étendus à l'instance de la classe.
Une requête Web => une session de persistance.
Pas une requête Web => une session de persistance par objet.

Affe
la source
2
J'aime cette explication car elle montre comment ThreadLocal doit être utilisé
kellyfj
4
Per-thread-per-instance peut être une sémantique utile, mais la plupart des utilisations de ce modèle impliqueraient tellement d'objets qu'il serait préférable d'utiliser a ThreadLocalpour contenir une référence à un ensemble de hachage qui mappe des objets à des instances par thread.
supercat
@optional Cela signifie simplement que chaque instance du non-statique ThreadLocalcontiendrait ses propres données locales de thread même si ces ThreadLocalinstances existent dans le même thread. Ce n'est pas nécessairement faux de faire cela - je suppose que c'est peut-être le modèle le moins populaire des deux
geg
17

Soit le rendre statique, soit si vous essayez d'éviter les champs statiques dans votre classe - faites de la classe elle-même un singleton et vous pouvez ensuite utiliser en toute sécurité le ThreadLocal de niveau d'instance tant que ce singleton est disponible globalement.

Adnan Memon
la source
12

Ça ne doit pas être. L'important est que ce soit un singleton.

irréprochable
la source
3

La raison en est que les variables sont accessibles via un pointeur associé au thread. Ils agissent comme des variables globales avec une portée de thread, donc static est la plus proche. C'est ainsi que vous obtenez l'état local des threads dans des choses comme pthreads, donc cela pourrait être un accident de l'historique et de l'implémentation.

Ukko
la source
1

Une utilisation d'un threadlocal sur une instance par thread est si vous voulez que quelque chose soit visible dans toutes les méthodes d'un objet et qu'il soit thread-safe sans synchroniser l'accès comme vous le feriez pour un champ ordinaire.

Chris Mawata
la source
1

Référez-vous à cela , cela donne une meilleure compréhension.

En bref, l' ThreadLocalobjet fonctionne comme une carte clé-valeur. Lorsque le thread invoqueThreadLocal get/set méthode, il récupère / stocke l'objet thread dans la clé de la carte et la valeur dans la valeur de la carte. C'est pourquoi différents threads ont une valeur copiée différente (que vous souhaitez stocker localement), car elle réside dans l'entrée de la carte différente.

C'est pourquoi vous n'avez besoin que d'une seule carte pour conserver toutes les valeurs. Bien que cela ne soit pas nécessaire, vous pouvez avoir plusieurs mappages (sans déclarer statique) pour conserver également chaque objet thread, ce qui est totalement redondant, c'est pourquoi la variable statique est préférée.

GMsoF
la source
-1

static final ThreadLocal les variables sont thread-safe.

staticrend la variable ThreadLocal disponible dans plusieurs classes pour le thread respectif uniquement. c'est une sorte de décaration de variable globale des variables locales de thread respectives à travers plusieurs classes.

Nous pouvons vérifier la sécurité de ce thread avec l'exemple de code suivant.

  • CurrentUser - stocke l'ID utilisateur actuel dans ThreadLocal
  • TestService- Service simple avec méthode - getUser()pour récupérer l'utilisateur actuel de CurrentUser.
  • TestThread - cette classe utilisée pour créer plusieurs threads et définir des userid simultanément

.

public class CurrentUser

public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();

public static ThreadLocal<String> getCurrent() {
    return CURRENT;
}

public static void setCurrent(String user) {
    CURRENT.set(user);
}

}

public class TestService {

public String getUser() {
    return CurrentUser.getCurrent().get();
}

}

.

import java.util.ArrayList;
import java.util.List;

public class TestThread {

public static void main(String[] args) {

  List<Integer> integerList = new ArrayList<>();

  //creates a List of 100 integers
  for (int i = 0; i < 100; i++) {

    integerList.add(i);
  }

  //parallel stream to test concurrent thread execution
  integerList.parallelStream().forEach(intValue -> {

    //All concurrent thread will set the user as "intValue"
    CurrentUser.setCurrent("" + intValue);
    //Thread creates a sample instance for TestService class
    TestService testService = new TestService();
    //Print the respective thread name along with "intValue" value and current user. 
    System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());

    try {
      //all concurrent thread will wait for 3 seconds
      Thread.sleep(3000l);
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    //Print the respective thread name along with "intValue" value and current user.
    System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
  });

}

}

.

Exécutez la classe principale TestThread. Production -

Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...

Résumé de l'analyse

  1. Le thread "principal" démarre et définit l'utilisateur actuel comme "62", parallèlement le thread "ForkJoinPool.commonPool-worker-2" démarre et définit l'utilisateur actuel comme "31", parallèlement le thread "ForkJoinPool.commonPool-worker-3" démarre et définit le courant l'utilisateur comme "81", parallèlement le thread "ForkJoinPool.commonPool-worker-1" démarre et définit l'utilisateur actuel comme "87" Start-main-> 62-> 62 Start-ForkJoinPool.commonPool-worker-2-> 31-> 31 Start-ForkJoinPool.commonPool-worker-3-> 81-> 81 Start-ForkJoinPool.commonPool-worker-1-> 87-> 87
  2. Tous ces fils ci-dessus dormiront pendant 3 secondes
  3. mainl'exécution se termine et imprime l'utilisateur actuel comme "62", parallèlement l' ForkJoinPool.commonPool-worker-1exécution se termine et imprime l'utilisateur courant comme "87", parallèlement l' ForkJoinPool.commonPool-worker-2exécution se termine et imprime l'utilisateur actuel comme "31", parallèlement l' ForkJoinPool.commonPool-worker-3exécution se termine et imprime l'utilisateur actuel comme "81"

Inférence

Les threads simultanés sont capables de récupérer les userids corrects même s'ils ont été déclarés comme "statique final ThreadLocal"

shijin raj
la source