Mon professeur dans une classe Java de niveau supérieur sur le threading a dit quelque chose dont je n'étais pas sûr.
Il a déclaré que le code suivant ne mettrait pas nécessairement à jour la ready
variable. 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
) tourne sur son propre processeur et ne partage donc pas les mêmes registres / cache / etc et un seul processeur ne mettra pas à jour l'autre.
Essentiellement, il a dit qu'il est possible que ce ready
soit mis à jour dans le thread principal, mais PAS dans le ReaderThread
, de sorte que ReaderThread
cela boucle indéfiniment.
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 number
variable est définie sur la valeur par défaut.
J'ai pensé 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. Est-ce que faire ready
volatile corrige 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;
}
}
la source
Réponses:
Il n'y a rien de spécial à propos des variables statiques en matière de visibilité. S'ils sont accessibles, n'importe quel thread peut les atteindre, vous êtes donc plus susceptible de rencontrer des problèmes de concurrence car ils sont plus exposés.
Il existe un problème de visibilité imposé par le modèle de mémoire de la JVM. Voici un article sur le modèle de mémoire et sur la façon dont les écritures deviennent visibles pour les threads . Vous ne pouvez pas compter sur les changements qu'un thread rend visibles aux autres threads en temps opportun (en fait, la JVM n'a aucune obligation de rendre ces modifications visibles pour vous, à aucun moment), sauf si vous établissez une relation qui se produit avant .
Voici une citation de ce lien (fournie dans le commentaire de Jed Wesley-Smith):
la source
Il parlait de visibilité et de ne pas être pris au pied de la lettre.
Les variables statiques sont en effet 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 y a deux copies de la variable.
Cet article présente une vue cohérente avec la façon dont il a présenté les informations:
Mais encore une fois, il s'agit simplement d'un modèle mental pour penser au threading et à la volatilité, pas littéralement au fonctionnement de la JVM.
la source
Fondamentalement, c'est vrai, mais en fait, le problème est plus complexe. La visibilité des données partagées peut être affectée non seulement par les caches CPU, mais aussi par l'exécution dans le désordre des instructions.
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, l'ajout
volatile
garantit la visibilité.la source
Ils sont bien sûr «partagés» 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 l'autre. Cela est vrai pour n'importe quelle 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 ne soient déclarées
volatile
ou que les écritures ne soient explicitement synchronisées.la source
Dans un seul chargeur de classe, les champs statiques sont toujours partagés. Pour étendre explicitement les données aux threads, vous souhaitez utiliser une fonction telle que
ThreadLocal
.la source
Lorsque vous initialisez la variable de type primitif statique, java default attribue une valeur aux variables statiques
lorsque vous définissez la variable comme ceci, la valeur par défaut de i = 0; c'est pourquoi il y a une possibilité de vous obtenir 0. alors le thread principal met à jour la valeur de boolean prêt à true. puisque ready est une variable statique, le thread principal et l'autre thread font référence à la même adresse mémoire, donc la variable ready change. ainsi le fil secondaire sort de la boucle while et imprime la valeur. lors de l'impression de la valeur initialisée, la valeur du nombre est 0. si le processus de thread a réussi la boucle while avant la variable numéro de mise à jour du thread principal. alors il est possible d'imprimer 0
la source
@dontocsata vous pouvez retourner voir votre professeur et le scolariser un peu :)
quelques notes du monde réel et peu importe ce que vous voyez ou ce qu'on vous dit. Veuillez noter que les mots ci-dessous concernent ce cas particulier dans l'ordre exact indiqué.
Les 2 variables suivantes résideront sur la même ligne de cache sous pratiquement n'importe quelle architecture connue.
Thread.exit
(thread principal) est garanti pour quitter etexit
est garanti pour provoquer une barrière de mémoire, en raison de la suppression du thread de groupe de threads (et de nombreux autres problèmes). (c'est un appel synchronisé, et je ne vois pas de moyen unique d'être implémenté sans la partie de synchronisation puisque le ThreadGroup doit également se terminer s'il ne reste aucun thread démon, etc.).Le thread démarré
ReaderThread
va maintenir le processus en vie puisqu'il ne s'agit pas d'un démon! Ainsiready
etnumber
seront vidés ensemble (ou le nombre avant si un changement de contexte se produit) et il n'y a aucune vraie raison de réorganiser dans ce cas au moins je ne peux même pas penser à un. Vous aurez besoin de quelque chose de vraiment bizarre pour voir autre chose42
. Encore une fois, je suppose que les deux variables statiques seront dans la même ligne de cache. Je ne peux pas imaginer une ligne de cache de 4 octets de long OU une JVM qui ne les attribuera pas dans une zone continue (ligne de cache).la source