Multithreading

Status
Nicht offen für weitere Antworten.

Maxga

Mitglied
Guten Tag,
wir hatte im Unterricht heute eine Aufgabe, 2 Methoden quasi parallel ausführen zu lassen.

Nun meine Frage, wie realisiert man das in Java? Wie realisiert man Multithreading? Irgendwer eine simple Erklärung oder einen guten Link zur Hand?

Danke,

MfG
 

Maxga

Mitglied
Ja das ist ja alles nur zum erstellen von Threads, soweit komme ich jetzt auch schon.
Nur wie schaffe ich es dass diese Threads quasi parallel verlaufen?

Wir arbeiten mit BlueJ zurzeit, und zwar mit dem FigurenProjekt, und da gibt es so eine Demo. Gezeichnet werden soll ein Quadrat, und darunter liegen 2 Kreise. Diese Kreise sollen sich quasi parallel so bewegen(einer nach links, einer rechts) dass sie nicht mehr vom Quadrat überdeckt werden.

Nun hab ich 1 Thread für das zeichnen und Bewegen des linken Kreises, 1 Thread für das zeichnen und Bewegen des rechten Kreises und 1 für das zeichnen des Quadrats.

Habe in einem einzelnen Thread diese 3 Threads initialisiert und starten lassen, und dann halt den 1 Thread mit den 3 Threads starten lassen.

Nun öffnet er aber 3 verschiedene Figurendemos, mit den Aktionen des einzelnen Threads, Wie kann ich das umgehen?

Ich hoffe ich hab mich verständlich ausgedrückt.

MfG
 

Marco13

Top Contributor
Verständlich? Ja. Jetzt ist jedem klar, dass du ein ziemliches Chaos produziert hast :wink:
Poste am besten deinen aktuellen Code.
 

Maxga

Mitglied
Ok, hier der Code, hoffentlich nicht zu unübersichtlich, und anscheinend scheint ziemlich viel falsch zu sein, naja daraus lernt man.

Code:
// Thread zur Zeichnung und Animation von Kreis1
public class Aufgabe4 implements Runnable
{
   private Kreis k1;
   
   public Aufgabe4()
   {
       k1 = new Kreis();    
   }
   
   public void zeichnen1()
   {
       k1.setzeDurchmesser(50);
       k1.setzePosition(25,100);
       k1.sichtbarMachen();
    }
    
    public void animation1()
    {
       k1.langsamHorizontalBewegen(-25);
    }
    
    public void run()
    {
        zeichnen1();
        animation1();
    }
}


// Thread zur Zeichnung und Animation von Kreis2
public class Aufgabe41 implements Runnable
{
    private Kreis k2;
    
    public Aufgabe41()
    {
        k2 = new Kreis();
    }
    
    public void zeichnen2()
    {
        k2.setzeDurchmesser(50);
        k2.setzePosition(75,100);
        k2.sichtbarMachen();
    }
    
    public void animation2()
    {
        k2.langsamHorizontalBewegen(25);
    }
    
    public void run()
    {
        zeichnen2();
        animation2();
    }
}


//Thread zur Zeichnung vom Quadrat
public class Aufgabe42 implements Runnable
{
    private Quadrat q1;
    
    public Aufgabe42()
    {
        q1 = new Quadrat();
    }
    
    public void zeichnen3()
    {
        q1.setzeSeitenlaenge(50);
        q1.setzePosition(50,100);
        q1.sichtbarMachen();
    }
    public void run()
    {
        zeichnen3();
    }
}


// Thread zur Gesamtlösung
public class Loesung1 implements Runnable
{
    public void run()
    {
        
        Thread a = new Thread(new Aufgabe4());
        Thread b = new Thread(new Aufgabe41());
        Thread c = new Thread(new Aufgabe42());        
        
            a.start();
            b.start();
            c.start();
    }
    public static void main(String[] args)
    {
        
        Thread z = new Thread(new Loesung1());
        
        z.start();
     }
}

So, hoffe ihr nehmt mich nicht zu hart ran ;)
Da kommen immer andere Fehlermeldung, manchmal z.B. ConcurrentModificationException manchmal z.B. NullPointerException, das Endbild sieht auch immer anders aus.

MfG
 

Marco13

Top Contributor
Poste mal auch noch Kreis und Quadrat - dort liegt vermutlich der Fehler.

(Am besten in solchen Fällen ist etwas, was man am Stück mit Copy&Paste kopieren und sofort compilieren und starten kann - OHNE sich um imports, fehlende Klassen und main-Methoden Gedanken machen zu müssen)
 

Maxga

Mitglied
Kreis und Quadrat waren von dem Projekt vorgegeben, denke nicht das dort ein Fehler liegt.
Werde sie trotzdem posten:

Quadrat:
Code:
import java.awt.Rectangle;

/**
 * Ein Quadrat, das manipuliert werden kann und sich selbst auf einer Leinwand
 * zeichnet.
 * 
 * @author Michael Kölling und David J. Barnes
 * @version 2006.03.30.
 * Für die IBBB-Tagung 2007 leicht modifiziert von Alexander Dietz (04.03.2007)
 */

public class Quadrat {
	private int laenge;

	private int xPosition;

	private int yPosition;

	private String farbe;

	private boolean istSichtbar;

	/**
	 * Erzeuge ein neues Quadrat mit einer Standardfarbe an einer
	 * Standardposition.
	 */
	public Quadrat() {
		laenge = 30;
		xPosition = 60;
		yPosition = 50;
		farbe = "rot";
		istSichtbar = false;
	}
	
	/**
     * Erzeuge ein neues Quadrat. Die Startwerte können eingegeben werden.
     * @param x  die x-Koordinate des linken oberen Eckpunktes 
     * @param y  die y-Koordinate des linken oberen Eckpunktes 
     * @param l  die Seitenlaenge des Quadrates
     * @param f  die Farbe des Quadrates ("rot", "gelb", "blau", "gruen", "lila", "schwarz")
     */
   public Quadrat(int x, int y, int l, String f) {
     xPosition = x;
     yPosition = y;
     laenge = l;
     farbe = f;  
     istSichtbar = false;
   }
	

  /**
   * Ändere die Seitenlaenge dieses Quadrates in 'l'.
   * @param l  die neue Seitenlaenge des Quadrates (groesser gleich 0)
   */
  public void setzeSeitenlaenge(int l)
  {
    loeschen();  
    if (l>=0)
       laenge = l;
    else
      System.out.println("Diese Eingabe ist nicht plausibel.");
    
    zeichnen();  
  }

  /**
     * Ändere die Position des Quadrates. 
     * @param x  die x-Koordinate des linken oberen Eckpunktes 
     * @param y  die y-Koordinate des linken oberen Eckpunktes 
    */
	
   public void setzePosition(int x, int y) {
        loeschen();
		if ((x>=0) && (y>=0)) {
		    xPosition=x;
		    yPosition=y;
		}    
		else 
		 System.out.println("Diese Eingabe ist nicht plausibel.");
	    zeichnen();
   }

	/**
	 * Mache dieses Quadrat sichtbar. Wenn es bereits sichtbar ist, tue nichts.
	 */
	public void sichtbarMachen() {
		istSichtbar = true;
		zeichnen();
	}

	/**
	 * Mache dieses Quadrat unsichtbar. Wenn es bereits unsichtbar ist, tue
	 * nichts.
	 */
	public void unsichtbarMachen() {
		loeschen();
		istSichtbar = false;
	}

	/**
	 * Bewege dieses Quadrat einige Bildschirmpunkte nach rechts.
	 */
	public void nachRechtsBewegen() {
		horizontalBewegen(20);
	}

	/**
	 * Bewege dieses Quadrat einige Bildschirmpunkte nach links.
	 */
	public void nachLinksBewegen() {
		horizontalBewegen(-20);
	}

	/**
	 * Bewege dieses Quadrat einige Bildschirmpunkte nach oben.
	 */
	public void nachObenBewegen() {
		vertikalBewegen(-20);
	}

	/**
	 * Bewege dieses Quadrat einige Bildschirmpunkte nach unten.
	 */
	public void nachUntenBewegen() {
		vertikalBewegen(20);
	}

	/**
	 * Bewege dieses Quadrat horizontal um 'entfernung' Bildschirmpunkte.
	 * @param entfernung
 
	 */
	public void horizontalBewegen(int entfernung) {
		loeschen();
		xPosition += entfernung;
		zeichnen();
	}

	/**
	 * Bewege dieses Quadrat vertikal um 'entfernung' Bildschirmpunkte.
	 */
	public void vertikalBewegen(int entfernung) {
		loeschen();
		yPosition += entfernung;
		zeichnen();
	}

	/**
	 * Bewege dieses Quadrat langsam horizontal um 'entfernung'
	 * Bildschirmpunkte.
	 */
	public void langsamHorizontalBewegen(int entfernung) {
		int delta;

		if (entfernung < 0) {
			delta = -1;
			entfernung = -entfernung;
		} else {
			delta = 1;
		}

		for (int i = 0; i < entfernung; i++) {
			xPosition += delta;
			zeichnen();
		}
	}

	/**
	 * Bewege dieses Quadrat langsam vertikal um 'entfernung' Bildschirmpunkte.
	 */
	public void langsamVertikalBewegen(int entfernung) {
		int delta;

		if (entfernung < 0) {
			delta = -1;
			entfernung = -entfernung;
		} else {
			delta = 1;
		}

		for (int i = 0; i < entfernung; i++) {
			yPosition += delta;
			zeichnen();
		}
	}

	

	/**
	 * Ändere die Farbe dieses Quadrates in 'neueFarbe'. Gültige Angaben sind
	 * "rot", "gelb", "blau", "gruen", "lila" und "schwarz".
	 */
	public void farbeAendern(String neueFarbe) {
		farbe = neueFarbe;
		zeichnen();
	}

	/**
	 * Zeichne dieses Quadrat mit seinen aktuellen Werten auf den Bildschirm.
	 */
	private void zeichnen() {
		if (istSichtbar) {
			Leinwand leinwand = Leinwand.gibLeinwand();
			leinwand.zeichne(this, farbe, new Rectangle(xPosition, yPosition,
					laenge, laenge));
			leinwand.warte(10);
		}
	}

	/**
	 * Lösche dieses Quadrat vom Bildschirm.
	 */
	private void loeschen() {
		if (istSichtbar) {
			Leinwand leinwand = Leinwand.gibLeinwand();
			leinwand.entferne(this);
		}
	}
}

Kreis:
Code:
import java.awt.geom.Ellipse2D;

/**
 * Ein Kreis, der manipuliert werden kann und sich selbst auf einer Leinwand
 * zeichnet.
 * 
 * @author Michael Kölling und David J. Barnes
 * @version 2006.03.30.
 * Für die IBBB-Tagung 2007 leicht modifiziert von Alexander Dietz (04.03.2007)
 */

public class Kreis {
	private int durchmesser;

	private int xPosition;

	private int yPosition;

	private String farbe;

	private boolean istSichtbar;

    
	/**
	 * Erzeuge einen neuen Kreis an einer Standardposition mit einer
	 * Standardfarbe.
	 */
   public Kreis() {
		durchmesser = 30;
		xPosition = 20;
		yPosition = 60;
		farbe = "blau";
		istSichtbar = false;
	}
  
	/**
     * Erzeuge einen neuen Kreis. Die Startwerte können eingegeben werden.
     * @param x  die x-Koordinate des Kreismittelpunktes
     * @param y  die y-Koordinate des Kreismittelpunktes
     * @param d  der Durchmesser des Kreises
     * @param f  die Farbe des Rechteckes ("rot", "gelb", "blau", "gruen", "lila", "schwarz")
     */
   public Kreis(int x, int y, int d, String f) {
     xPosition = x;
     yPosition = y;
     durchmesser = d;
     farbe = f;  
     istSichtbar = false;
   }
    
	/**
	 * Mache diesen Kreis sichtbar. Wenn es bereits sichtbar ist, tue nichts.
	 */
	public void sichtbarMachen() {
		istSichtbar = true;
		zeichnen();
	}

	/**
	 * Mache diesen Kreis unsichtbar. Wenn es bereits unsichtbar ist, mache
	 * nichts.
	 */
	public void unsichtbarMachen() {
		loeschen();
		istSichtbar = false;
	}

	
	
	/**
	 * Bewege diesen Kreis einige Bildschirmpunkte nach rechts.
	 */
	public void nachRechtsBewegen() {
		horizontalBewegen(20);
	}

	/**
	 * Bewege diesen Kreis einige Bildschirmpunkte nach links.
	 */
	public void nachLinksBewegen() {
		horizontalBewegen(-20);
	}

	/**
	 * Bewege diesen Kreis einige Bildschirmpunkte nach oben.
	 */
	public void nachObenBewegen() {
		vertikalBewegen(-20);
	}

	/**
	 * Bewege diesen Kreis einige Bildschirmpunkte nach unten.
	 */
	public void nachUntenBewegen() {
		vertikalBewegen(20);
	}

	/**
	 * Bewege diesen Kreis horizontal um 'entfernung' Bildschirmpunkte.
	 * @param entfernung  
	 */
	public void horizontalBewegen(int entfernung) {
		loeschen();
		xPosition += entfernung;
		zeichnen();
	}

	/**
	 * Bewege diesen Kreis vertikal um 'entfernung' Bildschirmpunkte.
	 * @param entfernung  
	 */
	public void vertikalBewegen(int entfernung) {
		loeschen();
		yPosition += entfernung;
		zeichnen();
	}

	/**
	 * Bewege diesen Kreis langsam horizontal um 'entfernung' Bildschirmpunkte.
	 * @param entfernung  
	 */
	public void langsamHorizontalBewegen(int entfernung) {
		int delta;

		if (entfernung < 0) {
			delta = -1;
			entfernung = -entfernung;
		} else {
			delta = 1;
		}

		for (int i = 0; i < entfernung; i++) {
			xPosition += delta;
			zeichnen();
		}
	}

	/**
	 * Bewege diesen Kreis langsam vertikal um 'entfernung' Bildschirmpunkte.
	 * @param entfernung  
	 */
	public void langsamVertikalBewegen(int entfernung) {
		int delta;

		if (entfernung < 0) {
			delta = -1;
			entfernung = -entfernung;
		} else {
			delta = 1;
		}

		for (int i = 0; i < entfernung; i++) {
			yPosition += delta;
			zeichnen();
		}
	}

	/**
	 * Ändere den Durchmesser dieses Kreises in 'neuerDurchmesser' (Angabe in
	 * Bildschirmpunkten). 'neuerDurchmesser' muss größer gleich Null sein.
	 * @param neuerDurchmesser  der neue Durchmesser 
	 */
	public void setzeDurchmesser(int neuerDurchmesser) {
		loeschen();
		if (durchmesser>=0)
		   durchmesser = neuerDurchmesser;
		else   
		   System.out.println("Diese Eingabe ist nicht plausibel.");
		zeichnen();
	}
	
  
    /**
     * Ändere die Position des Kreises
     * 'neuesX' und 'neuesY' müssen größer gleich Null sein.
     * @param neuesX  die neue x-Koordinate des Kreismittelpunktes
     * @param neuesY  die neue y-Koordinate des Kreismittelpunktes
     */
	public void setzePosition(int neuesX, int neuesY) {
	   loeschen();	
       if ((neuesX>=0) && (neuesY>=0)) {
		    xPosition=neuesX;
		    yPosition=neuesY;
		}    
		else 
		 System.out.println("Diese Eingabe ist nicht plausibel.");
	   zeichnen();
     }

	/**
	 * Ändere die Farbe dieses Kreises in 'neueFarbe'. Gültige Angaben sind
	 * "rot", "gelb", "blau", "gruen", "lila" und "schwarz".
	 * @param neueFarbe  
	 */
	public void farbeAendern(String neueFarbe) {
		farbe = neueFarbe;
		zeichnen();
	}

	/**
	 * Zeichne diesen Kreis mit seinen aktuellen Werten auf den Bildschirm.
	 */
	private void zeichnen() {
		if (istSichtbar) {
			Leinwand leinwand = Leinwand.gibLeinwand();
			leinwand.zeichne(this, farbe, new Ellipse2D.Double(xPosition,
					yPosition, durchmesser, durchmesser));
			leinwand.warte(10);
		}
	}

	/**
	 * Lösche diesen Kreis vom Bildschirm.
	 */
	private void loeschen() {
		if (istSichtbar) {
			Leinwand leinwand = Leinwand.gibLeinwand();
			leinwand.entferne(this);
		}
	}

}

Und dann noch Leinwand, die Klasse ist auch noch vorhanden:
Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * Leinwand ist eine Klasse, die einfache Zeichenoperationen auf einer
 * leinwandartigen Zeichenfläche ermöglicht. Sie ist eine vereinfachte Version
 * der Klasse Canvas (englisch für Leinwand) des JDK und wurde speziell für das
 * Projekt "Figuren" geschrieben.
 * 
 * 
 * @author: Bruce Quig
 * @author: Michael Kölling (mik)
 * @author: Axel Schmolitzky
 * 
 * 
 */
public class Leinwand {
	// Hinweis: Die Implementierung dieser Klasse (insbesondere die
	// Verwaltung der Farben und Identitäten der Figuren) ist etwas
	// komplizierter als notwendig. Dies ist absichtlich so, weil damit
	// die Schnittstellen und Exemplarvariablen der Figuren-Klassen
	// für den Lernanspruch dieses Projekts einfacher und klarer
	// sein können.

	private static Leinwand leinwandSingleton;

	/**
	 * Fabrikmethode, die eine Referenz auf das einzige Exemplar dieser Klasse
	 * zurückliefert. Wenn es von einer Klasse nur genau ein Exemplar gibt, wird
	 * dieses als 'Singleton' bezeichnet.
	 */
	public static Leinwand gibLeinwand() {
		if (leinwandSingleton == null) {
			leinwandSingleton = new Leinwand("BlueJ Figuren Demo", 300, 300,
					Color.white);
		}
		leinwandSingleton.setzeSichtbarkeit(true);
		return leinwandSingleton;
	}

	// ----- Exemplarvariablen -----

	private JFrame fenster;

	private Zeichenflaeche zeichenflaeche;

	private Graphics2D graphic;

	private Color hintergrundfarbe;

	private Image leinwandImage;

	private List<Object> figuren;

	private Map<Object, ShapeMitFarbe> figurZuShape;
	
	/**
	 * Erzeuge eine Leinwand.
	 * 
	 * @param titel
	 *            Titel, der im Rahmen der Leinwand angezeigt wird
	 * @param breite
	 *            die gewünschte Breite der Leinwand
	 * @param hoehe
	 *            die gewünschte Höhe der Leinwand
	 * @param grundfarbe
	 *            die Hintergrundfarbe der Leinwand
	 */
	private Leinwand(String titel, int breite, int hoehe, Color grundfarbe) {
		fenster = new JFrame();
		zeichenflaeche = new Zeichenflaeche();
		fenster.setContentPane(zeichenflaeche);
		fenster.setTitle(titel);
		zeichenflaeche.setPreferredSize(new Dimension(breite, hoehe));
		hintergrundfarbe = grundfarbe;
		fenster.pack();
		figuren = new ArrayList<Object>();
		figurZuShape = new HashMap<Object, ShapeMitFarbe>();
	}

	/**
	 * Setze, ob diese Leinwand sichtbar sein soll oder nicht. Wenn die Leinwand
	 * sichtbar gemacht wird, wird ihr Fenster in den Vordergrund geholt. Diese
	 * Operation kann auch benutzt werden, um ein bereits sichtbares
	 * Leinwandfenster in den Vordergrund (vor andere Fenster) zu holen.
	 * 
	 * @param sichtbar
	 *            boolean für die gewünschte Sichtbarkeit: true für sichtbar,
	 *            false für nicht sichtbar.
	 */
	public void setzeSichtbarkeit(boolean sichtbar) {
		if (graphic == null) {
			// erstmaliger Aufruf: erzeuge das Bildschirm-Image und fülle
			// es mit der Hintergrundfarbe
			Dimension size = zeichenflaeche.getSize();
			leinwandImage = zeichenflaeche.createImage(size.width, size.height);
			graphic = (Graphics2D) leinwandImage.getGraphics();
			graphic.setColor(hintergrundfarbe);
			graphic.fillRect(0, 0, size.width, size.height);
			graphic.setColor(Color.black);
		}
		fenster.setVisible(sichtbar);
	}

	/**
	 * Zeichne für das gegebene Figur-Objekt eine Java-Figur (einen Shape) auf
	 * die Leinwand.
	 * 
	 * @param figur
	 *            das Figur-Objekt, für das ein Shape gezeichnet werden soll
	 * @param farbe
	 *            die Farbe der Figur
	 * @param shape
	 *            ein Objekt der Klasse Shape, das tatsächlich gezeichnet wird
	 */
	public void zeichne(Object figur, String farbe, Shape shape) {
		figuren.remove(figur); // entfernen, falls schon eingetragen
		figuren.add(figur); // am Ende hinzufügen
		figurZuShape.put(figur, new ShapeMitFarbe(shape, farbe));
		erneutZeichnen();
	}

	/**
	 * Entferne die gegebene Figur von der Leinwand.
	 * 
	 * @param figur
	 *            die Figur, deren Shape entfernt werden soll
	 */
	public void entferne(Object figur) {
		figuren.remove(figur); // entfernen,falls schon eingetragen
		figurZuShape.remove(figur);
		erneutZeichnen();
	}

	/**
	 * Setze die Zeichenfarbe der Leinwand.
	 * 
	 * @param farbname
	 *            der Name der neuen Zeichenfarbe.
	 */
	public void setzeZeichenfarbe(String farbname) {
		if (farbname.equals("rot")) {
			graphic.setColor(Color.red);
		} else if (farbname.equals("schwarz")) {
			graphic.setColor(Color.black);
		} else if (farbname.equals("blau")) {
			graphic.setColor(Color.blue);
		} else if (farbname.equals("gelb")) {
			graphic.setColor(Color.yellow);
		} else if (farbname.equals("gruen")) {
			graphic.setColor(Color.green);
		} else if (farbname.equals("lila")) {
			graphic.setColor(Color.magenta);
		} else if (farbname.equals("weiss")) {
			graphic.setColor(Color.white);
		} else {
			graphic.setColor(Color.black);
		}
	}

	/**
	 * Warte für die angegebenen Millisekunden. Mit dieser Operation wird eine
	 * Verzögerung definiert, die für animierte Zeichnungen benutzt werden kann.
	 * 
	 * @param millisekunden
	 *            die zu wartenden Millisekunden
	 */
	public void warte(int millisekunden) {
		try {
			Thread.sleep(millisekunden);
		} catch (Exception e) {
			// Exception ignorieren
		}
	}

	/**
	 * Zeichne erneut alle Figuren auf der Leinwand.
	 */
	private void erneutZeichnen() {
		loeschen();
		for (Object figur : figuren) {
			figurZuShape.get(figur).draw(graphic);
		}
		zeichenflaeche.repaint();
	}

	/**
	 * Lösche die gesamte Leinwand.
	 */
	private void loeschen() {
		Color original = graphic.getColor();
		graphic.setColor(hintergrundfarbe);
		Dimension size = zeichenflaeche.getSize();
		graphic.fill(new Rectangle(0, 0, size.width, size.height));
		graphic.setColor(original);
	}

	/***************************************************************************
	 * Interne Klasse Zeichenflaeche - die Klasse für die GUI-Komponente, die
	 * tatsächlich im Leinwand-Fenster angezeigt wird. Diese Klasse definiert
	 * ein JPanel mit der zusätzlichen Möglichkeit, das auf ihm gezeichnet Image
	 * aufzufrischen (erneut zu zeichnen).
	 */
	private class Zeichenflaeche extends JPanel {
		private static final long serialVersionUID = 20060330L;

		public void paint(Graphics g) {
			g.drawImage(leinwandImage, 0, 0, null);
		}
	}

	/***************************************************************************
	 * Interne Klasse ShapeMitFarbe - Da die Klasse Shape des JDK nicht auch
	 * eine Farbe mitverwalten kann, muss mit dieser Klasse die Verknüpfung
	 * modelliert werden.
	 */
	private class ShapeMitFarbe {
		private Shape shape;

		private String farbe;

		public ShapeMitFarbe(Shape shape, String farbe) {
			this.shape = shape;
			this.farbe = farbe;
		}

		public void draw(Graphics2D graphic) {
			setzeZeichenfarbe(farbe);
			graphic.fill(shape);
		}
	}

}

So das wars,

MfG
 

Marco13

Top Contributor
Ach naja - der Fehler liegt nich unbedingt IN den Klassen, aber ggf. in ihrer Verwendung (und wie man sie richtig verwendet, erkennt man nicht, wenn man sie nicht kennt).

Sorry, ich muss jetzt mal ganz direkt fragen: In welchem Zusammenhang wurde diese Aufgabe gestellt?

Ich habe (eine Zeitlang) mit gutem Willen versucht, diese Aufgabe "vernünftig" zu lösen - und immer mit dem manchmal notwendigen Einfühlungsvermögen in die Intention des Aufgabenstellers. Aber ...die Klassen "Quadrat" und "Kreis" sind jeweils ca. 200 Zeilen lang, wobei 180 Zeilen bei beiden Klassen gleich sind :autsch: Über die Art, wie die Klassen implementiert sind (direkter Zugriff auf die Leinwand) könnte man streiten. Die Klasse "Leinwand" selbst tauchte schon in anderen Threads auf. Die Intention, die hinter dieser Klasse steht, ist akzeptabel: Einfaches Zeichnen, ohne dass man sich zu viele Gedanken über das "wie" machen muss. Trotzdem schöne Grüße an die Autoren dieser Klasse, die sich mal was über den Unterschied zwischen Factory und Singleton durchlesen sollten - und über die Verwendung des Schlüsselwortes "synchronized". (Aber vielleicht WOLLTEN sie die Leinwand-Klasse ja auch nicht Threadsafe machen. Kann ja sein....)

Wie auch immer.

Als erstes solltest du
public static Leinwand gibLeinwand()
ändern in
public static synchronized Leinwand gibLeinwand()
(das erste ist IMHO schlicht und einfach falsch. <- Punkt)

Dann im Konstruktor die Zeile
figuren = new ArrayList<Object>();
ändern in
figuren = java.util.Collections.synchronizedList(new ArrayList<Object>());

und die Methode erneutZeichnen ändern in
Code:
   private void erneutZeichnen() {
      loeschen();
      synchronized (figuren)
      {
          for (Object figur : figuren) {
             figurZuShape.get(figur).draw(graphic);
          }
      }
      zeichenflaeche.repaint();
   }
Die letzten beiden Punkte sind "quick-fixes" - nur der Versuch, das mit möglichst wenigen Änderungen vorerst zum Laufen zu bringen. Eigentlich sollte man die Klasse "Leinwand" lieber ... "vernünftig" machen - kann also gut sein, dass da irgendwo noch eine synchronisation fehlt. Falls es noch Exceptions gibt, sag' bescheid.

Jedefnalls solltest du damit deine bestehenden AufgabeXX-Klassen schon verwenden können. Zu denen sollte man vielleicht noch erwähnen, dass die Klassen- und Methodennamen keine Zahl am Ende haben sollten (d.h. nicht durchnummeriert sein sollten). Für das Quadrat braucht man eigentlich keinen Thread - das bewegt sich ja nicht. Und für die beiden Kreise... überleg' dir mal, wie eine Klasse aussehen könnte, die einen Namen wie "KreisAnimator" haben könnte (davon könnte man dann zwei Instanzen erstellen). Eigentlich sollte diese Klasse einfach "Animator" heißen können, aber ... das Problem dabei hatte ich ja weiter oben schon dezent angedeutet :roll:
 

Maxga

Mitglied
Danke für die Hilfe, jetzt funktioniert es.
Deine Anregungen finde ich gut, ich überarbeite das jetzt noch =)

In keinem Zusammenhang, die Aufgabe wurde einfach so zum Üben gestellt.

Nur eine Frage, schafft man es vielleicht, das einfach zu erklären, wieso die Änderungen notwendig sind, also was daran falsch ist? Würde dem Lehrer das nämlich auch gerne begründen können, wieso ich in den vorgegebenen Klassen rumpfusche.
Und ich verstehe auch gerne, was ich mache.

Vielen dank trotzdem =)

MfG
 

Marco13

Top Contributor
Die Begründungen:
public static synchronized Leinwand gibLeinwand()
Wenn das nicht da steht, können zwei Threads gleichzeitig die Methoden betreten - und dann werden ZWEI Instanzen von Leinwand erstellt (wobei es ja Ziel des Singleton-Patterns ist, dass es immer nur EINE Instanz geben kann)

Die anderen beiden Änderungen...

figuren = java.util.Collections.synchronizedList(new ArrayList<Object>());
synchronized (figuren) ...

... haben einen ähnlichen Zusammenhang: Die Liste muss synchronisiert sein, damit mehrere Threads damit arbeiten können. Anschaulich wird es beim Zeichnen: Wenn ein Thread in der Schleife
Code:
for (Object figur : figuren) {
    figurZuShape.get(figur).draw(graphic);
}
die Figuren zeichnet, und mittendrin legt ein anderer Thread ein neues Element in die Liste, dann kommt die obige Schleife durcheinander (genaugenommen der Iterator, der da durch die Liste läuft - das waren dann die ConcurrentModificationExceptions, die du bekommen hast). Wenn die Schleife in einem "synchronized (figuren)"-Block steht, und die "figuren" selbst eine synchronisierte Liste ist, dann ist sichergestellt, dass immer nur EIN Thread gleichzeitig etwas mit dieser Liste macht. Wenn DANN also ein Thread gerade in der Schleife ist, und ein anderer Thread will etwas in die Liste einfügen, dann wird letzterer so lange blockiert, bis der erste den "synchronized (figuren)"-Block verlassen hat, und kann erst danach das Element in die Liste legen.
 

ARadauer

Top Contributor
Code:
    public void run() 
    { 
        zeichnen1(); 
        animation1(); 
    }
hab mir jetzt nicht alles angesehen, aber kann es sein, dass hier der fehler ist? einmal zeichnen und bewegen ist noch keine animation... das würd ich in einer schleife machen mit einem kurzen warten nach dem bewegen....
 

Maxga

Mitglied
Ich probiere gerade es mit nur 1 Thread für die Bewegung des linken, und die Bewegung des rechten Kreises hinzubekommen, aber irgendwie klappt das nicht.

Keine Lösungen, aber irgendwer Lösungsansätze? =)

MfG
 

André Uhres

Top Contributor
Maxga hat gesagt.:
wir hatte im Unterricht heute eine Aufgabe, 2 Methoden quasi parallel ausführen zu lassen.
Versuch's mal so:
Code:
label1 = new JLabel("label1");
label2 = new JLabel("label2");
setLayout(null);
label1.setBounds(50, 50, 100, 30);
label2.setBounds(150, 80, 100, 30);
add(label1);
add(label2);
timer1 = new javax.swing.Timer(50, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        label1.setLocation(label1.getLocation().x + 1, label1.getLocation().y);
    }
});
timer2 = new javax.swing.Timer(50, new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        label2.setLocation(label2.getLocation().x + 1, label2.getLocation().y);
    }
});
timer1.start();
timer2.start();
 

Marco13

Top Contributor
Ja, @ARadauer - ich dachte auch erst: Häh? :autsch:
Dann hab' ich mir's genauer angesehen, und dachte? Hääähhh? :autsch:

Die "animation"-Methode delegiert indirekt an
Code:
public void langsamHorizontalBewegen(int entfernung) 
{
    ....
      for (int i = 0; i < entfernung; i++) {
         xPosition += delta;
         zeichnen();
      }
   }
und in der "zeichnen"-Methode steht dann (wieder indirekt) das "Thread.sleep()". D.h. der Thread, der die "animation"-Methode aufruft, bleibt beschäftigt, bis die Animation fertig ist. Andere Threads können dererweil munter die unsynchronisierten Daten zerbrezeln, aber ... (ich sag' ja: :autsch: ....)


@Maxga: Wegen der eben beschriebenen :autsch:-igen Struktur ist das schwierig. Das Setzen der Position löst ein Neuzeichnen und damit ein Warten (im selben Thread!!!) aus - man könnte natürlich leicht die Leinwand-Klasse und die Kreis- und Quadrat-Klasse ändern, damit das "besser" wird, aber ... (ja, es steht dir frei). Wenn das in einem Thread laufen sollte könnte man einfach abwechselnd die Positionen der Objekte ein Stück verändern (also NICHT langsamHorizontalBewegen aufrufen, sondern immer wieder setzePosition) ... oder so....
 

Maxga

Mitglied
Also könnte ich einfach machen, da ich weiß, beides muss 25 Einheiten bewegt werden:

for (int i = 0,i < 25; i++)
{
k1.lansamHorizontalBewegen(-1);
k2.langsamHorizontalBewegen(1);
}

??
Und sehe ich das richtig, der vom Projekt vorgegebene Quelltext ist einfach etwas vermurkst?

MfG
 

Marco13

Top Contributor
Ja, so sollte das gehen - ist nicht "schön", aber wenn es in das bestehende Schema gepresst werden soll... was will man da machen.

Der vorgegebene Quelltext ist
- teilweise fehlerhaft (fehlendes synchronized bei getInstance von Singleton)
- nicht Threadsicher (fehlende Synchronisation bei "figuren" usw.) - wobei das kein "Mangel" am Quelltext ist, sondern lediglich eine nicht-Eignung für eine bestimmte Art der Anwendung bewirkt
- Teilweise IMHO einfach scheußlich programmiert (Redundante Redundanzen zwischen Kreis und Quadrat)
und insgesamt darum
- nicht oder nur bedingt geeignet, um die Dinge zu machen, die du damit machen sollst....
 

Maxga

Mitglied
Ah ok, vielen Dank.

Jetzt funktioniert alles, nur ab und zu kriege ich ein paar Nullpointer-Exceptions, obwohl ich den Quelltext nicht verändere, nur alle 10 male oder so, wie kann das sein?

MfG
 

Marco13

Top Contributor
Bei exceptions immer Stacktrace posten. Ich habe aber die bestehenden Klassen NICHT komplett auf Threadsicherheit überprüft (oder hast du jetzt nurnoch den einen?) Ggf. kannst noch daran liegen, dass das GUI eigentlich im EDT gebaut werden müßte, aber vielleicht ist das (in diesem Fall) nicht so wichtig
 

Maxga

Mitglied
So, habe jetzt das Programm so oft aufgerufen, bis wieder die Fehlermeldung auftrat, ich poste sie mal hier:

Code:
Exception in thread "Thread-14" java.lang.NullPointerException
	at Leinwand.erneutZeichnen(Leinwand.java:196)
	at Leinwand.zeichne(Leinwand.java:132)
	at Quadrat.zeichnen(Quadrat.java:204)
	at Quadrat.sichtbarMachen(Quadrat.java:88)
	at Loesung.run(Loesung.java:24)
	at java.lang.Thread.run(Thread.java:619)
Exception in thread "Thread-15" java.lang.NullPointerException
	at Leinwand.erneutZeichnen(Leinwand.java:196)
	at Leinwand.zeichne(Leinwand.java:132)
	at Kreis.zeichnen(Kreis.java:206)
	at Kreis.sichtbarMachen(Kreis.java:56)
	at KreisAnimator.zeichnen(KreisAnimator.java:21)
	at KreisAnimator.run(KreisAnimator.java:32)
	at java.lang.Thread.run(Thread.java:619)

Hoffe ihr könnt damit was anfangen =)
Vom aufbau meiner Klasse hat sich nicht wirklich was verändert, bis auf die Namen der beiden Threads, und das der Thread fürs Quadrat nicht mehr exestiert, und die Befhle fürs Quadrat einfach in die Klasse geschrieben wurden.
Bis auf die Fehlermeldung die alle 5-10 male mal vorkommt, klappt die Aufgabenstellung jetzt, und es wird auch nur einmal angezigt, und net auf 3 verschiedenen Leinwänden.

MfG
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
buzzlightyeah multithreading Java Basics - Anfänger-Themen 4
sserio Frage zu Threading - Multithreading Java Basics - Anfänger-Themen 2
I Threads Multithreading, Producer/Consumer, notify() Java Basics - Anfänger-Themen 6
M Mehre Dateien parallel kopieren mit Multithreading Java Basics - Anfänger-Themen 8
kilopack15 Verzweiflung wegen Berechnung mit Multithreading Java Basics - Anfänger-Themen 1
P Multithreading in Java Java Basics - Anfänger-Themen 9
N Threads Read-Modify-Write Problem bei Multithreading (philosopher dining problem) Java Basics - Anfänger-Themen 5
R Threads Multithreading Java Basics - Anfänger-Themen 15
Z Verständnisfrage zum Multithreading Java Basics - Anfänger-Themen 3
T Threads MultiThreading NullPointerException Java Basics - Anfänger-Themen 7
K Frage bzgl. Multithreading Java Basics - Anfänger-Themen 5
B Multithreading und eigene Queue entwickeln Java Basics - Anfänger-Themen 3
C Multithreading, Methoden sichern Java Basics - Anfänger-Themen 5
P Hilfe bei MultiThreading; Einige Fragen. Java Basics - Anfänger-Themen 14
S OOP Multithreading Java Basics - Anfänger-Themen 5
B Multithreading Java Basics - Anfänger-Themen 5
0din Multithreading und stop Java Basics - Anfänger-Themen 5
P Singletons und Multithreading Java Basics - Anfänger-Themen 11
U Anfängerfrage - Multithreading Java Basics - Anfänger-Themen 8
H Multithreading Java Basics - Anfänger-Themen 7
I Multithreading (Prüfungsvorbereitung) Java Basics - Anfänger-Themen 6
G UI friert bei Multithreading ein Java Basics - Anfänger-Themen 3
J Multithreading mit einer TextArea Java Basics - Anfänger-Themen 29
JFeel-x Multithreading in awt Java Basics - Anfänger-Themen 2
L NullpointerException wegen wahrscheinlichem Multithreading Java Basics - Anfänger-Themen 4
Z Multithreading Java Basics - Anfänger-Themen 2
P Multithreading Java Basics - Anfänger-Themen 22

Ähnliche Java Themen

Neue Themen


Oben