Threads: Datenverlust absichtlich erzeugen

lajao

Mitglied
Nunja, Datenverlust ist ja eigentlich das, was jeder Programmierer verhindern will. Ich schaffs aber nicht, diesen absichtlich herbeizuführen 🙁

Idee:
1. Mein Prog erzeugt ein Objekt namens Zwischenspeicher, dort wird ein einzelner int-Wert gespeichert
2. Danach produziert es 25 Threads, die jeweils eine Zahl im Speicher ablegen, danach 2 Sekunden warten, danach den Wert wieder einlesen und vergleichen.

Ich hoffe nun, dass ein zweiter Thread seinen Wert ablegt, während der erste Thread nach dem Ablegen am Warten ist. Allerdings will mir das Programm diesen Gefallen nicht tun. Vielmehr scheinen die Threads zu warten, bis der vorherige fertig ist. Hier der Code:

[Java]public class Zwischenspeicher{
private int wert;

public int getWert(){
return wert;
}

public void setWert(int wert){
this.wert = wert;
}
}[/code]

simpler Speicher, mit Datenfeld, getter und setter.

Java:
public class ZahlThread extends Thread{

  Zwischenspeicher speicher;
  
  public ZahlThread(Zwischenspeicher speicher){
    this.speicher = speicher;
  }


//public synchronized void run()
  public void run(){
    try{
      int uebergabeWert;
      int erhalten;
      speicher.setWert((uebergabeWert = (int)(Math.random() * 1000)));
    //this.wait(2000);
      this.sleep(2000);

      if((erhalten = speicher.getWert()) == uebergabeWert){
        System.out.println(uebergabeWert + " = " + erhalten);
      }else{
        System.out.println(uebergabeWert + " != " + erhalten);
      }
    }catch(InterruptedException e){

    }

  }
}

"Bauplan" für die Threads. Die Kommentare stehen für die Variante, die den Datenverlust verhindern sollten.

Java:
public class TestZahlThread{

  public static void main(String[] args){

    Zwischenspeicher speicher = new Zwischenspeicher();
    ZahlThread[] threads = new ZahlThread[25];

    for(int i = 0; i < 25; i++){
      threads[i] = new ZahlThread(speicher);
      threads[i].run();
      System.out.println((i+1) + ". Durchlauf der while-Schleife");
    }
    
    System.out.println("main() wurde erfolgreich durchlaufen");
  }
}

Die "Start"-Klasse


Die Main-Methode wird schrittweise abgearbeitet, sie ist erst fertig, wenn jeder Thread fertig ist. Doch genau das ist nicht der Sinn der Threads, sondern Gleichzeitigkeit?
Wo habe ich hier meinen Denkfehler?
 
Das heißt, du willst ein Desyncronisationsproblem schaffen?

Java:
public class Desyn
{
	private static int j;
	
	public static void main(String[] args)
	{
		Thread t1 = new Thread() {
			public void run()
			{
				for(int i = 0; i < Integer.MAX_VALUE / 2; i++)
					j++;
			}
		};
		t1.start();
		
		Thread t2 = new Thread() {
			public void run()
			{
				for(int i = 0; i < Integer.MAX_VALUE / 2; i++)
					j++;
			}
		};
		t2.start();
		
		try
		{
			t1.join();
			t2.join();
		}
		catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		
		System.out.println(String.format("Erwartetes Ergebnis: %s", Integer.MAX_VALUE - 1));
		System.out.println(String.format("Reeles Ergebnis: %s", j));
	}
}
 
Zuletzt bearbeitet:
Code:
threads[i].run();
ist falsch.

Code:
threads[i].start();
startet wirklich neue Threads.
 
@Volvagia
Ich denke schon^^
Bei j++ wird ja eigentlich auch der ursprüngliche Wert j von der Klasse abgeholt, verarbeitet und zurückgegeben, wobei nicht berücksichtigt wird, ob j dazwischen vom anderen Thread überschrieben worden ist (zumindest ist das die Erklärung, die ich mir zusammengebastelt habe). Sowas hab ich hier auch vor, einfach mit nem separaten "Speicher"-Objekt statt der Klassenvariabel.

Wenn ich den Rest richtig verstehe, ruft
Code:
threads[i].run()
die run-Methode des Threads als normale Methode auf und wird demnach genau gleich verarbeitet wie beispielsweise
Code:
speicher.getWert()
, also wie wenn eine stinknormale andere Methode aus einer normalen anderen Klasse aufgerufen wird?

Die start()-Methode allerdings ruft zwar ebenfalls die run()-Methode des Threads auf, tut dies aber auf "Thread-Weise"? Also kann ein Thread nur durch start() in den Konkurrenzkampf um Prozessorleistung geschickt werden?
 
Die Methoden werden beide Stinknormal aufgerufen, machen aber unterschiedliches.
run() hast du ja selber geschrieben, da ist kein Code, der einen neuen Thread erzeugt drunter.
Also gibt es auch keinen neuen Thread.
start() hingegen erzeugt zunächst einen neunen Thread und führt dann run() in diesem aus:
Java:
    /**
     * Causes this thread to begin execution; the Java Virtual Machine 
     * calls the <code>run</code> method of this thread. 
     * <p>
     * The result is that two threads are running concurrently: the 
     * current thread (which returns from the call to the 
     * <code>start</code> method) and the other thread (which executes its 
     * <code>run</code> method). 
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
	 * This method is not invoked for the main method thread or "system"
	 * group threads created/set up by the VM. Any new functionality added 
	 * to this method in the future may have to also be added to the VM.
	 *
	 * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
	    stop0(throwableFromStop);
	}
    }

    private native void start0();
 
Bei j++ wird ja eigentlich auch der ursprüngliche Wert j von der Klasse abgeholt, verarbeitet und zurückgegeben, wobei nicht berücksichtigt wird, ob j dazwischen vom anderen Thread überschrieben worden ist (zumindest ist das die Erklärung, die ich mir zusammengebastelt habe). Sowas hab ich hier auch vor, einfach mit nem separaten "Speicher"-Objekt statt der Klassenvariabel.

Genau. In etwa (Cache/Multicores/SMP etc. außer acht gelassen) läuft das so ab:

Thread A:
...
Kopiere j (4) vom RAM im Register.
Addiere j (4) im Register um 1 (5).
Unterbreche.

Thread B:
Kopiere j (4) vom RAM im Register.
Addiere j (4) im Register um 1 (5).
Schreibe j (5) vom Register im RAM.
...
Unterbreche.

Thread A:
Schreibe j (5) vom Register im RAM.
...

2 Additionen, 1 sichtbare Änderung.
 

Zurück
Oben