Threads Nebenläufigkeit - Busy Waiting: Exotisches Problem

Kaibear

Aktives Mitglied
Moin moin werte Community,

in der FH arbeiten wir derzeit an Nebenläufigkeit und hatten dazu heute ein kleines Beispiel von "Busy Waiting"

Doch da gibt es ein kleines Problem. Die unten abgebildete Klasse Car soll solange warten, wie der Wert "hasToWait" true ist. Durch "TrafficLight" wird ein Car-Objekt instanziert und über die dort enthaltene NotifyCar-Methode soll das Attribut "hasToWait" geändert werden und das Auto weiterfahren.

Für eine Wartezeit zum Sehen des Effekts haben wir einen Thread.Sleep implementiert und hier wirds merkwürdig.

Bei allen sleep()-Werten über 0 bekomm ich nicht die Ausgabe "Auto fährt weiter". Wenn ich allerdings in der While-Schleife der crossTrafficLight()-Methode editiere, sieht das anders aus.

Wenn während der While-Schleife eine System.out.println("...") Line jedes Mal während des Thread.sleeps ausgeführt wird, gibt es die gewünschte Ausgabe. Ich dachte also, eine for-Schleife dazwischen packen (so seltsam wie das Problem schon ist). Da gab es wieder keine Ausgabe. Wenn ich zur for-Schleife aber jedes Mal eine Syso-Line ausgebe, geht es wieder?!

Bei meinem Prof lief im Übrigen alles ohne For-Schleife, die While-Schleife blieb leer.

Wo liegt der Fehler?

Car-Klasse
Java:
package trafficlight;

public class Car implements Runnable {

	private boolean hasToWait = true;

	public void run() {
		this.crossTrafficLight();
	}

	public void crossTrafficLight() {
		while(hasToWait){ for(int i = 0; i<20; i++){System.out.println("123");}} // Busy waiting
		System.out.println("Auto fährt über Ampel");
	}

	public void notifyCar() {
		this.hasToWait = false;
		System.out.println("Test");
	}
}
TrafficLight-Klasse
Java:
package trafficlight;

public class TrafficLight implements Runnable {
	private Car car;

	public TrafficLight(Car car) {
		this.car = car;
	}

	@Override
	public void run() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.car.notifyCar();
	}

}
Main-Klasse
Java:
package trafficlight;

public class Main {

	public static void main(String[] args){
		Car car = new Car();
		TrafficLight tl = new TrafficLight(car);
		
		new Thread(car).start();
		new Thread(tl).start();
	}
	
}
 

eMmiE

Bekanntes Mitglied
Habe dasselbe Problem.
Ersetze vorerst System.out.println("...");
durch
System.out.print("");

dann spamt er dir die Meldungen nicht voll
 

Kaibear

Aktives Mitglied
Ich habe die Sourcen des Profs getestet. Ich habe diese also 1:1 und es tritt dieser Fehler auf. Bei meinem Prof hingegen läuft alles wie gefordert. Es wird dieselbe JRE verwendet. ;(
 

wef34fewrg

Aktives Mitglied
Hallo Kaibear.

Versuch mal folgendes:

"private volatile boolean hasToWait = true;", oder arbeite mit synchronized Methoden und wait/notify (falls ihr das schon hattet). Versuch mal dahinter zu steigen wieso das so ist (was volatile) macht und erklärs mir bzw. ich sag dirs, wenn du es nicht verstehst.

Übrigens. Wenn das ein Beispiel von eurem Prof ist, dann klopf ihm/ihr mal ganz gewaltig auf die Finger, denn:

Oberstes Gebot, welches du dir gleich auf die Netzhaut tätowieren kannst:
Wenn von mehreren Threads gleichzeitig auf ein Objekt zugegriffen wird, wobei mindestens ein Thread einen schreibenden Zugriff hat, IMMER mit synchronized arbeiten!
 
Zuletzt bearbeitet:

Kaibear

Aktives Mitglied
Ahh es funktioniert!

Super!

Synchronized hatten wir schon und ist soweit auch verstanden, dass volatile auf atomarer Ebene hierfür ist. Das mit dem Fingerklopfen mache ich aber dennoch frage ich mich weshalb es auf manchen Systemen ohne Probleme (auch bei mehreren Starts) klappt, aber bei mir nicht?
 

wef34fewrg

Aktives Mitglied
Ahh es funktioniert!

Super!

Synchronized hatten wir schon und ist soweit auch verstanden, dass volatile auf atomarer Ebene hierfür ist. Das mit dem Fingerklopfen mache ich aber dennoch frage ich mich weshalb es auf manchen Systemen ohne Probleme (auch bei mehreren Starts) klappt, aber bei mir nicht?

Dazu kann ich dir zwei Teilantworten geben.

Die erste Antwort auf die Frage warum es bei dir (übrigens auch bei mir, habs grad mal kurz ausprobiert) nicht geht. Volatile hat in dem Fall nichts mit dem atomaren Zugriff zu tun.
Um nicht jedes Mal auf den Hauptspeicher zuzugreifen (kostet alles Zeit) wird bei Daueraufrufen wie deinem, die Variable einfach in einem Register geparkt, um so die Zugriffszeiten zu optimieren.
Schreibt jetzt dein AmpelThread die Variable im HS in "false" um, so bekommt der Car Thread das gar nicht mit, weil sein lokaler Speicher die Gültigkeit der Variable mit dem Registerinhalt vergleicht (der ja nachwievor true ist).
Volatile verhindert diese Art der Codeoptimierung und "zwingt" Threads dazu, sich die Werte noch einmal neu aus dem Speicher zu holen. synchronized hat übrigens den selben Effekt wie volatile.

Die zweite Teilantwort heisst. Ich weiß es nicht. Ich weiß nicht, wieso sich Java genötigt fühlt auf einigen Systemen diese Optimierung durchzuführen und auf anderen nicht.
Logik oder gar Determinismus darfst du im Bereich Multithreading von Java nicht erwarten. Darum musst du dich selber kümmern.

Bei dem Beispiel hätte er eigentlich sofort danach erwähnen sollen, wie man es richtig macht. ;)
 
Zuletzt bearbeitet:

Ähnliche Java Themen

Neue Themen


Oben