web-dev-qa-db-fra.com

Les variables statiques sont-elles partagées entre les threads?

Mon professeur de niveau supérieur Java), la classe sur le filetage disait quelque chose dont je n’étais pas sûr.

Il a déclaré que le code suivant ne mettrait pas nécessairement à jour la variable ready. Selon lui, les deux threads ne partagent pas nécessairement la variable statique, en particulier dans le cas où chaque thread (thread principal versus ReaderThread) s'exécute sur son propre processeur et ne partage donc pas les mêmes registres/cache/etc et un processeur ne mettra pas à jour l'autre.

Essentiellement, il a dit qu'il est possible que ready soit mis à jour dans le fil principal, mais PAS dans ReaderThread, de sorte que ReaderThread sera mis en boucle à l'infini.

Il a également affirmé qu'il était possible pour le programme d'imprimer 0 ou 42. Je comprends comment 42 pourrait être imprimé, mais pas 0. Il a mentionné que ce serait le cas lorsque la variable number est définie sur la valeur par défaut.

Je pensais qu'il n'était peut-être pas garanti que la variable statique soit mise à jour entre les threads, mais cela me semble très étrange pour Java. Rendre ready volatile corrige-t-il ce problème?

Il a montré ce code:

public class NoVisibility {  
    private static boolean ready;  
    private static int number;  
    private static class ReaderThread extends Thread {   
        public void run() {  
            while (!ready)   Thread.yield();  
            System.out.println(number);  
        }  
    }  
    public static void main(String[] args) {  
        new ReaderThread().start();  
        number = 42;  
        ready = true;  
    }  
}
91
dontocsata

Les variables statiques n’ont rien de spécial en matière de visibilité. S'ils sont accessibles, tous les threads peuvent les atteindre. Il est donc plus probable que vous rencontriez des problèmes de simultanéité, car ils sont plus exposés.

Il existe un problème de visibilité imposé par le modèle de mémoire de la machine virtuelle Java. Voici un article sur le modèle de mémoire et sur la manière dont les écritures deviennent visibles pour les threads . Vous ne pouvez pas compter sur les modifications qu'un thread rend visibles pour les autres threads de manière opportune (en fait, la machine virtuelle Java n'a aucune obligation de rendre ces modifications visibles pour vous, à quelque moment que ce soit), sauf si vous établissez un se passe-avant la relation .

Voici une citation de ce lien (fourni dans le commentaire de Jed Wesley-Smith):

Le chapitre 17 de la spécification de langage Java définit la relation passe-avant pour les opérations en mémoire, telles que la lecture et l'écriture de variables partagées. Les résultats d'une écriture par un thread sont visibles pour une lecture. par un autre thread uniquement si l'opération d'écriture a lieu avant l'opération de lecture. Les constructions synchronisées et volatiles, ainsi que les méthodes Thread.start () et Thread.join (), peuvent former des relations événements-avant.

  • Chaque action dans un thread se produit-avant chaque action dans ce thread qui vient plus tard dans l'ordre du programme.

  • Un déverrouillage (sortie synchronisée de bloc ou de méthode) d'un moniteur a lieu avant chaque verrouillage ultérieur (entrée de bloc ou de méthode synchronisée) de ce même moniteur. Et, parce que la relation arrive-avant est transitive, toutes les actions d'un thread avant le déverrouillage se produisent avant toutes les actions postérieures à tout thread verrouillant ce moniteur.

  • Une écriture dans un champ volatile se produit avant chaque lecture ultérieure de ce même champ. Les écritures et les lectures de champs volatiles ont des effets de cohérence de la mémoire similaires à ceux des moniteurs entrant et sortant, mais n'entraînent pas de verrouillage par exclusion mutuelle.

  • Un appel à démarrer sur un thread se produit avant toute action dans le thread démarré.

  • Toutes les actions d'un thread se produisent-avant qu'un autre thread ne soit renvoyé avec succès d'une jointure sur ce thread.

74
Nathan Hughes

Il parlait de visibilité et de ne pas être pris trop littéralement.

Les variables statiques sont effectivement partagées entre les threads, mais les modifications apportées dans un thread peuvent ne pas être immédiatement visibles par un autre thread, ce qui donne l'impression qu'il existe deux copies de la variable.

Cet article présente une vue cohérente avec la manière dont il a présenté l'info:

Tout d’abord, vous devez comprendre un petit quelque chose à propos du modèle de mémoire Java). J’ai eu un peu de mal à l’expliquer brièvement et bien. À ce jour, la meilleure façon de penser de le décrire si vous l’imaginez de cette façon:

  • Chaque thread dans Java se déroule dans un espace mémoire séparé (cela est clairement faux, alors soyez avec moi sur celui-ci).

  • Vous devez utiliser des mécanismes spéciaux pour garantir la communication entre ces unités d'exécution, comme vous le feriez avec un système de transmission de messages.

  • Les écritures de la mémoire qui se produisent dans un thread peuvent "fuir" et être vues par un autre thread, mais cela n'est en aucun cas garanti. Sans communication explicite, vous ne pouvez pas garantir que les écritures sont vues par d'autres threads, ni même par l'ordre dans lequel elles sont vues.

...

thread model

Mais encore une fois, c’est simplement un modèle mental pour penser au filetage et à l’instabilité, et non littéralement comment fonctionne la machine virtuelle.

35
Bert F

En gros, c’est vrai, mais le problème est plus complexe. La visibilité des données partagées peut être affectée non seulement par les caches de la CPU, mais également par l'exécution d'instructions dans le désordre.

Par conséquent Java définit un modèle de mémoire , qui indique dans quelles circonstances les threads peuvent voir l'état cohérent des données partagées.

Dans votre cas particulier, ajouter volatile garantit la visibilité.

12
axtavt

Ils sont "partagés" bien sûr dans le sens où ils se réfèrent tous les deux à la même variable, mais ils ne voient pas nécessairement les mises à jour de chacun. Ceci est vrai pour toute variable, pas seulement statique.

Et en théorie, les écritures effectuées par un autre thread peuvent sembler être dans un ordre différent, à moins que les variables soient déclarées volatile ou que les écritures soient explicitement synchronisées.

8
biziclop

Dans un chargeur de classe unique, les champs statiques sont toujours partagés. Pour définir explicitement les données sur les threads, vous souhaiterez utiliser une fonction telle que ThreadLocal.

4
Kirk Woll

Lorsque vous initialisez une variable de type primitif statique Java par défaut attribue une valeur aux variables statiques

public static int i ;

lorsque vous définissez la variable comme celle-ci, la valeur par défaut de i = 0; C'est pourquoi il est possible d'obtenir 0. le thread principal met alors à jour la valeur de boolean ready to true. puisque ready est une variable statique, le thread principal et l’autre thread référençant la même adresse mémoire afin que la variable ready soit modifiée. de sorte que le fil secondaire sort de la boucle while et imprime une valeur. lors de l'impression, la valeur initialisée de number est 0. si le processus de thread a abouti en boucle avant la variable numéro de mise à jour du thread principal. alors il y a une possibilité pour imprimer 0

2
NuOne