Synchronisierung von Threads die Antwort?

L

LurchiDerLurch

Gast
Hallo

Mein Problem bezieht sich nicht direkt auf das Synchronisieren von Threads.

Zuerst mal der Hintergrund: Ich hab ein Programm mit Kollisionserkennung und bewegten Objekten. Die Klasse Main startet hierfür einen Thread (20ms pro Durchlauf), der sich laufend um Kollisionserkennung, Bewegung und Zeichnen der Objekte kümmert.

Die Objekte bieten die Methode setPos(x,y), welche vom Main Thread laufend verwendet wird. (die Objekte bewegen sich ja schließlich die ganze/meiste Zeit)

Jetzt kommt aber mein Problem:

Ich hab zu Testzwecken einen Maus Event Handler eingebaut, mit dem ich die Position eines Objekts verändern kann. Er verwendet also auch wieder die setPos(x,y) Methode.

Jetzt kann es aber vorkommen, dass die Positionsänderung nicht funktionieren will, weil ja der Main Thread ununterbrochen reinfunkt. Mittels System.out.println("...") hab ich mir anzeigen lassen, wann der Main Thread bzw. der Event Handler die setPos(x, y) Methode verwenden.
-> Der Event Handler genau 3 mal so schnell. Trotzdem setzt sich der Main Thread durch.

Wenn man jetzt bedenkt, dass ich vielleicht später das Objekt (zB nicht über den Event Handler) an eine andere Position schieben will, möchte ich sicher gehen, dass das einwandfrei funktioniert!


Also: Bevor ich mir jetzt irgendetwas abenteuerliches ausdenke, um das Problem zu lösen, wollte ich wissen, wie man das normalerweise in der Spieleprogrammierung handhabt. Muss das irgendwie in verschiedenen Threads laufen?


Ich wäre um jede Hifle dankbar :)

Lurchi
 

Volvagia

Top Contributor
Muss auf jedem Fall syncronisiert werden.
Hat aber wohl nicht direkt etwas mit dem Problem zu tun, dafür wäre ein wenig mehr Infos oder ein wenig Source hilfreich.

Kann aber passieren, dass wenn der Gameloop die Position ändert, zwischendurch interrupted wird und der MouseHandler (ich nehme mal an EDT) ebenfalls ändern will eine Änderung verlohren geht.

Java:
private Lock SET_POS_LOCK = new ReentrantLock();

public void setPos(int x, int y)
{
	SET_POS_LOCK.lock();
	try
	{
		this.x = x;
		this.y = y;
	}
	finally
	{
		SET_POS_LOCK.unlock();
	}
}

Oder du machst den Lock direkt beim Aufruf. Dann muss jede Klasse zwar den Lock kennen, und der Source wird sich wohl insgesamt verlängern, du bist aber flexibler.
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Je nachdem, worum es genau geht, wie flexibel das werden soll, wie groß das ganze aufgezogen ist usw: Für sowas kann man auch in Erwägung ziehen, eine "Kommando-Warteschlange" zu verwenden. GANZ grob formuliert: Man erstellt eine Queue mit Runnable-Objekten drin, und läßt die vom Haupt-Spiel-Thread abarbeiten - Pseudo(!) code:
Java:
class Game
{
    private Queue<Runnable> queue = ...

    public void addCommand(Runnable r) { queue.add(r); }

    void runningInGameThread()
    {
        while (!queue.isEmpty())
        {
            Runnable r = queue.take();
            r.run();
        }
        gameLogic();
    }
}

Pseudocode, weil man sich dazu am besten der java.util.concurrent Klassen bedient - das Reinlegen muss ja auch Thread-safe sein. Jedenfalls hat das den Vorteil, dass man beliebige Kommandos (nicht nur das Setzen der Position) im Game-Thread machen lassen kann. Das gleiche Prinzip wird bei SwingUtilities.invokeLater verwendet.
 
L

LurchiDerLurch

Gast
Muss auf jedem Fall syncronisiert werden.
Hat aber wohl nicht direkt etwas mit dem Problem zu tun, dafür wäre ein wenig mehr Infos oder ein wenig Source hilfreich.

Das ist jetzt die Frage. Vorab erst mal ein wenig Source, damit wir vom gleichen Thema reden ;-)

Java:
public class Main extends JPanel implements Runnable, MouseMotionListener, MouseListener, KeyListener
{
...
//startet einen neuen Thread
addMouseListener(this);
Thread thread = new Thread(this);
thread.start();
...

	@Override
	public void run() 
	{		
		while(running)
		{
			repaint();

			doLogic();

			try
			{
				Thread.sleep(20);
			}
			catch(InterruptedException e)
			{
				
			}
		}
	}

	private void doLogic()
	{	
		//Logic World
		world.logic();
		
		//Kollsion
		for(ObjectFactory obj1 : objects)
		{
...
		}
		
		//Logic Objects
		for(ObjectFactory obj:objects)
		{
			obj.logic();
		}
	}
...
}

Dieser Thread kümmert sich also um die Objekte und zeichnet die auch.

Die Methode setPos für die Klasse ObjectFactory (sprich für ein beliebiges Objekt)
Java:
	public void setPos(double xNew, double yNew)
	{
		this.x = xNew;
		this.y = yNew;
	}

Meinst du wirklich es liegt daran, dass die Methode nicht mit einem Lock versehen ist?
 
L

LurchiDerLurch

Gast
Erst mal sorry für Doppelpost, vielleicht sollte ich mich ja mal anmelden ^^

Mir kamen aber 2 Ideen.

1) Sollte vielleicht jeder Thread, bevor er die Position ändert, warten, bis der Main Thread mit einem Durchlauf fertig ist? Danach ist ja eigentlich die Bahn frei und der Main Thread müsste demnach warten, bis die anderen Threads die Position aktualisiert haben (falls die denn auf setPos zugreifen)
Dann läuft der Main Thread wieder mit den korrekten (eventuell geänderten) Positionen durch?

2) Der Main Thread greift auf eine setPos Methode zu, die eine geringere Priorität hat, wie die setPos Methode, auf die alle anderen Threads zugreifen würden? Sprich: Im Zweifelsfall die Position der anderen Threads nehmen? :/ hmm...

Sind jetzt auch nur 2 Gedanken gewesen ^^

Deswegen wollte ich ja wissen, wie man das normalerweise regelt. Ist sicher ein bekannter Konflikt?

Lurchi
 

Volvagia

Top Contributor
Ich sehe diesen Thread nichts zeichnen.
1) ist eigendlich nichts anderes als eine syncronisation. 2) verstehe ich nicht. Wie kann man einer Methode eine Priorität zuweißen?
 
L

LurchiDerLurch

Gast
Ich sehe diesen Thread nichts zeichnen.
1) ist eigendlich nichts anderes als eine syncronisation. 2) verstehe ich nicht. Wie kann man einer Methode eine Priorität zuweißen?

Macht es aber. Hab den Code nur abgekürzt.

1) Sollte ich das also so machen? Dann muss ich mich mal dazu schlau machen, wie ich das am besten mach.

2) War ja nur ne Idee ^^ Heißt ja nicht automatisch, dass es möglich oder sinnvoll ist...
 

Volvagia

Top Contributor
Es gibt afaik 3 Möglichkeiten zu syncronisieren.

1) Du verwendest eine Klasseninstanz, die speziell dafür zuständig ist, wie in meinem Beispiel.
2) Du verwendest irgend eine Instanz, und benutzt syncronized(instance). Dann kommt kein Thread rein, solange mit der Instanz gelockt wurde, außer wenn dieser Thread gelockt hat.
3) Ist eigendlich keine Syncronisierungsmethode, aber du könntest alles von einen Thread abarbeiten lassen, wie z. B. mit einer LinkedBlockingQueue wie in Marcos Beispiel.
Oder direkt den EDT abarbeiten lassen, dann kann wärend der Abarbeitung aber nicht neu gezeichnet werden, außerdem benutzten vordefinierter Listener afaik alle den EDT.

Java:
SwingUtilities.invokeLater(new Runnable() {
	public void run()
	{
		setPos(x, y);
	}
});
 
Zuletzt bearbeitet:

Ähnliche Java Themen

Neue Themen


Oben