web-dev-qa-db-fra.com

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

Je lisais le JavaDoc pour Threadlocal ici

http://Java.Sun.com/j2se/1.5.0/docs/api/Java/lang/ThreadLocal.html

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

Mais ma question est la suivante: pourquoi ont-ils choisi de le rendre statique (généralement)? Cela rend les choses un peu confuses d’avoir l’état "par thread" mais les champs sont statiques?

94
kellyfj

Parce que s'il s'agissait d'un champ de niveau instance, il s'agirait en réalité de "Per Thread - Per Instance", pas simplement d'un "Per Thread" garanti. Ce n'est pas normalement la sémantique que vous recherchez. 

Habituellement, il contient des objets tels que des objets liés à une conversation utilisateur, une requête Web, etc. Vous ne voulez pas qu'ils soient également sous-appliqués à l'instance de la classe.
Une demande Web => une session de persistance.
Pas une seule requête Web => une session de persistance par objet.

111
Affe

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

14
Adnan Memon

Ce n'est pas obligé. L'important est que ce soit un singleton.

10
irreputable

La raison en est que les variables sont accessibles via un pointeur associé au thread. Elles agissent comme des variables globales avec une étendue de thread, par conséquent statique est la solution la plus proche. C’est ainsi que vous obtenez l’état local des threads dans des tâches telles que pthreads. Cela pourrait donc être un simple accident de l’historique et de la mise en œuvre.

3
Ukko

Reportez-vous à this , cela vous donnera une meilleure compréhension.

En bref, l’objet ThreadLocal fonctionne comme une mappe valeur-clé. Lorsque le thread appelle la méthode ThreadLocalget/set, il récupère/stocke l'objet de thread dans la clé de la carte et la valeur dans la valeur de la carte. C'est pourquoi différents threads ont différentes valeurs copiées (que vous souhaitez stocker localement), car elles résident 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 mappes (sans déclarer static) pour conserver chaque objet thread, ce qui est totalement redondant, raison pour laquelle la variable statique est préférée.

0
GMsoF

Une utilisation pour 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 que celui-ci soit thread-safe sans synchroniser l'accès à celui-ci comme vous le feriez pour un champ ordinaire. 

0
Chris Mawata

static final ThreadLocal les variables sont thread-safe.

static rend la variable ThreadLocal disponible dans plusieurs classes pour le thread respectif uniquement. c'est une sorte de définition de variable globale des variables locales de thread respectives sur plusieurs classes.

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

  • CurrentUser - stocke l'ID utilisateur actuel dans ThreadLocal
  • TestService - Service simple avec la méthode - getUser() pour extraire l'utilisateur actuel de CurrentUser.
  • TestThread - cette classe utilisée pour créer plusieurs threads et définir des ID utilisateur 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 . Sortie - 

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é d'analyse 

  1. Le fil "principal" démarre et définit l'utilisateur actuel sur "62", parallèlement le fil "ForkJoinPool.commonPool-worker-2" démarre et définit l'utilisateur actuel sur "31", parallèlement sur le fil "ForkJoinPool.commonPool-worker-3" l'utilisateur en tant que "81", en parallèle "le thread ForkJoinPool.commonPool-worker-1" démarre et définit l'utilisateur actuel en "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 threads vont dormir pendant 3 secondes
  3. main exécution se termine et affiche l'utilisateur actuel sous la forme "62". Parallèlement, l'exécution ForkJoinPool.commonPool-worker-1 prend fin et affiche l'utilisateur actuel sous la forme "87". Parallèlement, l'exécution ForkJoinPool.commonPool-worker-2 s'achève et affiche l'utilisateur actuel sous la forme "31". __ parallèlement ForkJoinPool.commonPool-worker-3 l'exécution se termine et affiche l'utilisateur actuel sous "81"

Inférence

Les threads simultanés sont capables d'extraire les ID utilisateur corrects même s'ils ont été déclarés comme "statique final ThreadLocal"

0
shijin raj