public class GuardedBlock {
private boolean guard = false;
private static void threadMessage(String message) {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
public static void main(String[] args) {
GuardedBlock guardedBlock = new GuardedBlock();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
guardedBlock.guard = true;
threadMessage("Set guard=true");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadMessage("Start waiting");
while (!guardedBlock.guard) {
//threadMessage("Still waiting...");
}
threadMessage("Finally!");
}
});
thread1.start();
thread2.start();
}
}
I was learning concurrency through java essentials tutorial. Got to guarded blocks and tried to test it. There is one thing I cannot understand.While loop is infinite, but if you uncomment threadMessage line everything works fine. Why?
3 Réponses :
Vous avez oublié de déclarer Si vous envisagez la déclaration de votre champ Comme Dans de tels cas, la valeur de maintenant pourquoi ici une pâte du code de l'un des et le remarque la synchronisation. p> p> gardien code> comme booléen volatile. p>
volatile code>, vous ne direz pas le JVM que ce champ peut être vu par plusieurs threads, ce qui est le cas dans votre exemple. p> Garde code> sera lu une seule fois et provoquera une boucle infinie. Il sera optimisé à quelque chose comme ceci (sans impression): p> system.out.println code> changer ce comportement? Parce que le écrit code> est synchronisé qui force les filets à non pas en cache se lit. p> printLn code> méthode de Imprimanteam code> utilisé par system.out.println code>: p> écriture code> méthode: p>
La raison pour laquelle votre boucle tandis que votre boucle est infinie est que la condition Permettez-moi de copier le besoin d'utiliser le volatil dans Java de Wikipedia lui-même: P>
Dans toutes les versions de Java, il existe un ordre global sur les lectures et écrit à une variable volatil. Cela implique que chaque fil accédant à un champ volatille lira sa valeur actuelle avant de continuer fort>, au lieu de (potentiellement) à l'aide d'une valeur mise en cache. P>
blockQuote>
espère que cela aide. P> ! gardéblock.guard code> est toujours vrai. Cela signifie gardéblock.guard = true; code> défini dans le thread 1 n'est pas défini pour le fil 2, et cela se produit parce que vous n'utilisez pas la protection variable comme volatile p >
La solution de Jean-Francois est la bonne: vous devez absolument avoir une sorte de synchronisation lorsque les threads accèdent à une variable partagée, que ce soit via J'ajouterais également que votre Vous voudrez peut-être explorer le variable de condition approche de traiter avec plusieurs threads affectés par une seule condition partagée. Java a beaucoup d'outils de niveau supérieur pour cela dans le chaque Voici ce que votre code ressemblerait à utiliser cette approche: p> Si vous regardez l'objet code> code> API, vous verrez qu'il y a aussi un volatile code>, synchronisé code>, etc. tandis que code> la boucle est à savoir ce qu'on appelle occupé En attente - c'est-à-dire, testez à plusieurs reprises une condition dans un réglage simultané. La boucle serrée de l'attente occupée dans ce code peut porter la CPU. Au moins, ses effets sur les ressources du système seront imprévisibles. P> java.util.concurrent code> bibliothèque, mais il est bon de connaître les méthodes d'API plus anciennes de niveau inférieur, d'autant plus que vous travaillez directement avec thread code> instances déjà . p> Objet code> a attendre () code> et notifier () code> méthodes. L'objet code> représente la condition ou au moins le moniteur associé à celui-ci. La méthode wait () code> est appelée dans un tandis que code> boucle qui teste la condition et bloque le thread d'appel jusqu'à certains autres appels de threads notifier () code> . Ensuite, tous les fils d'attente seront éveillés et ils auront tous concurrencer la serrure et la possibilité de tester la condition à nouveau. Si la condition reste vraie à ce stade, tous les threads procéderont. P> public class GuardedBlock {
private boolean guard = false;
private static void threadMessage(String message) {
System.out.println(Thread.currentThread().getName() + ": " + message);
}
public static void main(String[] args) throws Exception {
GuardedBlock guardedBlock = new GuardedBlock();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
synchronized (guardedBlock) {
guardedBlock.guard = true;
guardedBlock.notifyAll();
}
threadMessage("Set guard=true");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
threadMessage("Start waiting");
while (!guardedBlock.guard) {
synchronized (guardedBlock) {
try {
guardedBlock.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
threadMessage("Finally!");
}
});
thread1.start();
thread2.start();
thread2.join();
System.out.println("Done");
}
}
synchronisé code> mot-clé.) li>
Guard CODE> La condition est testée à l'intérieur d'un pendant que code> boucle, mais cette boucle bloque pendant le attendre () code> appel. La seule raison pour laquelle il reste un pendant que code> est de gérer des situations dans lesquelles de nombreux threads et la condition change de plusieurs reprises. Ensuite, la condition doit être réutilisée lorsqu'un thread est réveillé, dans le cas où un autre fil modifie la condition dans le minuscule écart entre le réveil et la reprise de la serrure. Li>
Condition code> est défini sur true code> (via notifier () code> appels.) Li>
() code> appel.) li>
ul> notifier () code> méthode. Il est plus simple d'utiliser toujours notifier () code>, mais si vous souhaitez comprendre la différence entre ces deux méthodes, Voir ceci alors post . p> p>
Qu'est-ce que vous essayez de faire exactement? Les modifications apportées à
Guard code> ne seront pas visibles pour d'autres threads si elle n'est pas volatile.Lorsque vous commencez cette ligne, il ne reste plus rien à l'intérieur de votre temps, et Java est suffisamment intelligent pour ignorer la boucle complète. Donc, c'est aussi bon qu'il n'y a pas de boucle.
Que voulez-vous dire par tout fonctionne bien? Est-ce qu'il imprime toujours? finalement ?
Est-ce que cela fonctionne si vous modifiez
Guard code> versGuard volatile code>? De plus, une boucle d'attente occupée est mauvaise, je ne vous recommande pas de les utiliser. Pensez à utiliserattendre code> etnotify code> à la place.@AgRAWAL: Cela ne peut pas être vrai. Si c'était le cas, OP ne ferait pas l'expérience d'une boucle infinie. Au contraire, comme Akhil_mittal dise,
Guard code> n'est pasvolatile code>. Donc, sa valeur n'est pas garantie d'être synchronisée entre les threads. L'ajout de la ligne code> threadmessage code> doit déclencher une sorte de barrière de mémoire qui se produit pour aider à synchroniser la vue du thread de la valeur code> de la valeur code>. Mais c'est un comportement très peu fiable.