synchronized & volatile

hdi

Top Contributor
Hey,

also ich hab schon vor ein paar Wochen etwas bzgl. volatile gefragt. Aber so komplett hab ich diese ganze Geschichte - auch in Verbindung mit synchronized - noch nicht begriffen. Ich sag euch mal was ich darüber denke und ihr sagt mir ob es richtig ist :D

Also synchronized kann man bei Primitiven nicht benutzen. D.h. man sollte sich dafür ein Objekt nehmen auf das man synchronisiert - entweder die Primitive reinkapseln oder halt "extern" schauen dass alle Threads auf so ein Pseudo-Objekt synchen beim Zugriff auf das Primtive.

Bsp:

Java:
class Test{
 public static int count = 0;
 public static boolean running = true;
 public static Object lock = new Object();
}

class SomeThread extends Thread{
 public void run(){
     while(Test.running){
        synchronized(Test.lock){
            if(Test.count < 1000){
               Test.count++;
            }
         }
   
        if(getSomeRandomNumber() == 234){
            Test.running = false;
        }
      }
}

Test.count kann jetzt bei mehreren SomeThreads niemals 1001 werden wg. synch auf dem lock, right?
Aber was ist wenn Thread 1 Test.running auf false setzt - das bekommt Thread 2 glaub ich nich zwangsläufig mit oder? Dafür müsste man dann Test.running volatile machen, stimmt das?

Generell ist mir noch nicht ganz klar wie volatile und synchronized zusammenspielen. Wenn ich zB die if-Abfrage oben auch in den synch-Block des "lock" schiebe, führt das dazu dass diese Variablen (zB Test.running) von jedem Thread bei jedem Durchlauf neu geladen werden? Eigentlich haben sie ja mit dem "lock" nichts zu tun oder.

Also wann muss ich bei Primitiven volatile nehmen? IMMER wenn mehrere Threads darauf zugreifen? Und was bringt ein volatile bei Objekten? Wird durch Punkt-Notation nicht immer neu derefernziert, auch ohne volatile?

Okay also nochmal kurz und knapp, im Moment verstehe ich das ganze so. Im Bezug auf Multi-Thread wenn mehrere Threads eine Resource nutzen...
- ...und sie ein Objekt ist dann darauf synchen. Alle Werte die ausgelesen werden sind die aktuellsten weil durch Punkt-Notation die aktuellen Wert jedesmal neu ausgelesen werden
- ...und sie ein Primitive ist, dann kann ich nicht direkt synchen sondern mach mir ein Pseudo-Objekt dass ich als Lock nutze
- ...allerdings garantiert das nicht dass ein Thread beim Zugriff auf das Primtive tatsächlich den aktuellen Wert nimmt, dazu müsste ich es volatile machen

???:L

Danke!
 

hdi

Top Contributor
So okay also ich hab mir jetzt mal ne Übersicht geschaffen und ich glaube ich hab das ganze jetzt schon mehr verstanden, aber es gibt noch immer Fragen. Ich texte mal drauf los und ihr hackt ein wenn ich Mist rede ;)

volatile hat erstmal gar nix mit synchen zu tun. Das bezieht sich nämlich nur auf die Sichtbarkeit, Stichwort Java Memory Model aus dem Video. Generell ist es so, dass wenn ich nicht synche, Änderungen auf einer Variablen in anderen Threads nicht zwangsläuftig sichtbar werden. Und zwar egal ob das jetzt Primitive sind oder Objekt-Variablen. Right?

Nun kann ich volatile verwenden, damit ich solche Sichtbarmachungen garantiere. Allerdings eigentlich nur auf Primitiven, da ich bei Objekt-Variablen die Referenz manipulieren müsste, sprich neue Zuweisung und das will/kann man ja wohl meistens nicht.
Zusammen mit einer atomaren Operation ist also ein volatile eine synchronisierte (da atomare) und sicherlich sichtbar gemachte Änderung. Nur dass es performanter ist als ein synchronized.

Synchronized brauch ich aber schon mal auf jeden Fall wenn ich ein "Operation Compound" habe, um Race Conditions zu vermeiden. Und synchronized enthält immer die Effekte eines volatile, richtig?

Jetzt frag ich mich aber inwiefern. Im Video gab es sinngemäß das Bsp:

Java:
private int count;
public synchronized void changeCount()  { ... }
public int size() { return count; }

Mehrere Threads verändern den count, und einer ruft die size() auf. Da diese nicht synched ist kann es sein dass da ein alter Wert gelesen wird. Das könnte ich beheben durch volatile beim count. Okay.
Oder - und das versteh ich nicht ganz - indem ich auch die size() synchronisiere. Warum?

Was genau passiert wenn ein synchronized Block betreten wird? Da werden dann alle Attribute des Objekts refreshed oder was? Und was ist wenn das nicht-Primitive sind? Sagen wir ich hab als Attribut ne komplexe Klasse die intern nochmal n Array von Arrays hat usw und da drin wurde jetzt von einem Thread etwas geändert. Und das ist dann refreshet wenn ich den Getter für dieses Attribut von nem anderen Thread aufrufe wenn ich synche? Ich meine wieweit geht das denn in die "Tiefe" damit?

Also im Endeffekt ist das jetzt noch das große Fragezeichen über meinem Kopf: Was genau wird refresht wenn ich einen synchronized Block betrete?

Jetzt noch ein Bsp mit statics:
Java:
public class Test {

	public static int count;
	public static boolean running;
	
	public static void main(String[] args) {
		running = true;
		count = 0;
		new SomeThread().start();
		new SomeThread().start();
	}
}

public class SomeThread extends Thread {

	@Override
	public void run() {
		while (Test.running) {
			if (Test.count < 100000) {
				Test.count++;
			}
			System.out.println(Test.count);
		}
	}
}

Also erstmal ist das nicht synchronisiert, und solange die Variablen static bleiben sollen kann ich das nur synchen indem ich mir ein Pseudo-Objekt erstelle, denn ein volatile würde nix bringen da Test.count++ nicht atomar ist. D.h. ich muss sowas machen:

Java:
public void run() {
		while (Test.running) {
                        synchronized(Test.lockObject){
			     if (Test.count < 100000) {
			       	Test.count++;
			     }
			    System.out.println(Test.count);
                         }
 		}
	}

Aber das unterbindet lediglich das Switchen innerhalb der Logik der Threads, aber noch nicht dass zB wenn ein Thread Test.running auf false setzt, die anderen das mitkriegen. Oder überhaupt: Jeder liest noch immer evtl. seinen eigenen count aus, denn das lockObject hat ja eig. gar nix mit dem count zu tun, ist ja ne ganz andere Variable.

Also müsste ich zusätzlich ein volatile vor running und count klatschen.

Zusammenfassung: Bei statischen Primitiven brauch ich immer volatile, und sofern die Mainupulationen nicht atomar sind außerdem noch irgendein Objekt auf dem ich synchronisiere wenn ich diese Manipulationen mache.
Bei nicht-statischen Variablen kann ich mir ein volatile immer sparen solange ich auf das Objekt, in dem diese Variablen als Attribute definiert sind, synche - denn wenn man auf ein Objekt syncht werden alle Attribute - bis in die "hintersten Ecken" sozusagen - refresht.

Richtig oder falsch?
 
Zuletzt bearbeitet:

eRaaaa

Top Contributor
Mehrere Threads verändern den count, und einer ruft die size() auf. Da diese nicht synched ist kann es sein dass da ein alter Wert gelesen wird. Das könnte ich beheben durch volatile beim count. Okay.
Oder - und das versteh ich nicht ganz - indem ich auch die size() synchronisiere. Warum?

Was genau passiert wenn ein synchronized Block betreten wird? Da werden dann alle Attribute des Objekts refreshed oder was?

fast...

Synchronized Methods (The Java™ Tutorials > Essential Classes > Concurrency)
----------------
[...] when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.
----------------
 

hdi

Top Contributor
Ja okay.. ich würd jetzt zwar noch immer für nix meine Hand ins Feuer legen aber ich glaub so einigermaßen hab ich das jetzt verstanden mit dem ganzen sync und volatile zeug :)

Danke!
 

hdi

Top Contributor
Ach, noch eine klitzekleine Frage, wenn ich so ne typische Lifetime Variable hab:

edit: Hab grad gesehen genau das wird in dem Artikel gezeigt! JA man muss volatile machen ;)


Java:
class C extends Thread{

  private boolean alive;

  public void setAlive(boolean alive){
       this.alive=alive;
  }

  public void run(){
       alive = true;
       while(alive){...}
   }
}

und ich ruf von nem anderen Thread setAlive(false) auf, ist das dann für diesen hier sichtbar? D.h. muss ich auch meine eigenen Attribute volatile machen, bzw. erstellt der andere Thread dann irgendwie ne lokale Kopie davon in seinem Cache und setzt dann lediglich diese auf false?
 
Zuletzt bearbeitet:

FArt

Top Contributor
JA man muss volatile machen
Nein, muss man nicht. volatile ist die günstigere Variane, aber das Setzen und Auswerten des Flags über den gleichen Monitor zu synchronisieren führt zu dem selben Ziel.

Am besten beschreibt den Zusammenhang tatsächlich die JSR-133 (die Stellen sind einigermaßen verständlich). Wenn man "Happens-Before-Relationship" verstanden hat (und wann dies garantiert wird), dann wird klar was volatile und synchronized gemeinsam haben... und was nicht.
 

hdi

Top Contributor
Ok nochmal zum Abschluss bitte, spielt es jetzt ne Rolle auf was ich eig. synche?

Bsp:

Java:
synchronized(something){
    a = 4;
    b++;
}

Die Änderungen von a und b sind hier also für einen anderen Thread der diesen Block betrifft immer sichtbar, obwohl das Objekt nicht direkt etwas damit zu tun hat, ja? Weil Thread 1 beim Verlassen des synch die Werte von a und b in den main memory flusht und Thread 2 beim Betreten die Zugriffe auf dem main memory macht?
 

FArt

Top Contributor
Vergiss die Sache mit dem Speicher usw.

Ja, wer auch immer den synchronisierten Block betritt ist allein da drin und sieht die Änderungen von Threads, die ihn davor betreten und wieder verlassen haben.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J Synchronized Probleme Allgemeine Java-Themen 7
D ReentrantLock oder Synchronized ? Allgemeine Java-Themen 3
B Threads synchronized Allgemeine Java-Themen 3
OnDemand Threads und synchronized Allgemeine Java-Themen 9
N Best Practice Semi-Synchronized Zugriff Allgemeine Java-Themen 0
E Verständnisfrage zu synchronized-Blöcken Allgemeine Java-Themen 3
J yield() Aufruf in einer synchronized-Methode Allgemeine Java-Themen 13
A Frage zu Synchronized Allgemeine Java-Themen 5
R Synchronized - auf welchem Objekt Allgemeine Java-Themen 16
R synchronized methode rekursiv aufrufen Allgemeine Java-Themen 5
P synchronized Allgemeine Java-Themen 4
S komplexe synchronized bedingungen Allgemeine Java-Themen 6
G synchronized Allgemeine Java-Themen 7
J Threads und synchronized Allgemeine Java-Themen 18
G zwei mal synchronized Allgemeine Java-Themen 5
J synchronized block mit this und wait() Allgemeine Java-Themen 5
M Verständnis "synchronized" Allgemeine Java-Themen 4
T Thread synchronized Allgemeine Java-Themen 5
Kr0e Synchronized Allgemeine Java-Themen 4
K synchronized und notify / notifyAll Allgemeine Java-Themen 8
G synchronized-Sclüsselwort: welche Reihenfolge zum Betreten? Allgemeine Java-Themen 6
R synchronized "gegen sich selbst" Allgemeine Java-Themen 5
R ConcurrentModificationException trotz synchronized? Allgemeine Java-Themen 12
R Thread-Problem, und synchronized bringt nix Allgemeine Java-Themen 4
J synchronized (bitte beantworten, urgent! danke) Allgemeine Java-Themen 11
H Ein synchronized Block ausreichend? Allgemeine Java-Themen 6
G synchronized Klasse? Allgemeine Java-Themen 6
G synchronized + threads Allgemeine Java-Themen 12
A deadlocks bei synchronized Allgemeine Java-Themen 3
K vector, synchronized und mehrere methoden Allgemeine Java-Themen 3
J Collections, Locks und volatile ? Allgemeine Java-Themen 1
M Parallele Programmierung: volatile Variable nimmt ungewöhnlichen Wert an Allgemeine Java-Themen 3
P volatile bei threadsafe Klassen nötig? Allgemeine Java-Themen 3
E Multithreading and volatile Allgemeine Java-Themen 10
hdi volatile & Thread#sleep/yield - Versteh ich nich Allgemeine Java-Themen 14
B Volatile Frage: Reicht es nur den Singleton als volatile zu deklarieren? Allgemeine Java-Themen 4
J volatile Verständnisfrage Allgemeine Java-Themen 6

Ähnliche Java Themen

Neue Themen


Oben