Dziwne zachowanie poruszającego się obiektu

0

Napisałem sobie taki prosty programik, w którym sterujemy kulką. Tzn klikamy w dowolny punkt na ekranie i w to miejsce "wędruje" kulka. W trakcie jej przemieszczania możliwe jest wskazanie nowego miejsca docelowego i wówczas kulka od razu zmienia kierunek na aktualny. Cała operacja trasowania i odświeżania pozycji jest zawarta w osobnym wątku.

Problem jest następujący: Gdy w trakcie wykonywania wątku poruszającego kulką podamy nowe współrzędne docelowe (klikniemy w jakieś miejsce) kulka przyspiesza. Kiedy znowu szybko klikniemy - przyspiesza jeszcze bardziej. Zmienia kierunek/trasę we właściwy sposób ale porusza się z większą szybkością - nie mam pojęcia dlaczego :/

Kluczowy fragment MouseListenera:

public void mouseReleased (MouseEvent e)
		{
			dMovThrd.setXY(e.getX(), e.getY());
			
			if(! thrd1.isAlive())
			{
				Thread thrd1 = new Thread(dMovThrd);
				thrd1.start();
			}
		}
 

Wątek poruszający kulką:

 public class DiscMovingThread implements Runnable
	{
		Thread thrd;
		MyPanel jpnl;
		int x=0, y=0; // współrzędne docelowe
		int discX, discY; // aktualne wpsółrzędne
		
		public DiscMovingThread (String name, MyPanel panel)
		{
			thrd = new Thread(this, name);
			jpnl = panel;
			discX = jpnl.returnDiscX();
			discY = jpnl.returnDiscY();
		}
		
		public void setXY(int x, int y)
		{
			this.x = x;
			this.y = y;
		}
		
		public void run ()
		{
			while (discX != x || discY != y)
			{
				if(discX != x)
				{
					if(x-discX > 0)
					{
						jpnl.setDiscXY(discX+1, discY);
						discX ++;
					}
					else
					{
						jpnl.setDiscXY(discX-1, discY);
						discX --;
					}
				}
						
				if(discY != y)
				{
					if(y-discY > 0)
					{
						jpnl.setDiscXY(discX, discY+1);
						discY ++;
					}
					else
					{
						jpnl.setDiscXY(discX, discY-1);
						discY --;
					}
				}
				
				try
				{
					Thread.sleep(5);
					jpnl.repaint();
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
	}

Jakby ktoś chciał to tutaj jest cały (nieduży) kod: http://wklej.org/id/992964/

0

bo poprzedni wątek nadal działa zmieniając współrzędne kulki. po każdym kliknięciu musisz zatrzymywać stary wątek.

0

Nie wiem za bardzo ŁF co masz na myśli. Jaki poprzedni wątek? Ogólnie w moim programie zawsze działa tylko jeden wątek DiscMovingThread na raz. W przedstawionym fragmencie MouseListenera jest if który sprawdza czy ten wątek "żyje" - jeżeli tak to tylko przekazuje mu nowe współrzędne, jeżeli nie to go tworzy. Poza tym nawet jeżeli by działało klika jednocześnie to itak by nie tłumaczyło przyspieszania kulki - Tak mi się wydaje, może jest inaczej albo Cię nie zrozumiałem - w takim wypadku prosiłbym o dokładniejsze wytłumaczenie.

0

Nie sądzę. To co napisałeś jest dość dziwne i nie powinno się skompilować (ale jestem programistą C#, a nie java).

                        if(! thrd1.isAlive()) // odwołujesz się do zewnętrznego scope, znaczy thrd1 jest prawdopodobnie polem obiektu
                        {
                                Thread thrd1 = new Thread(dMovThrd); // tworzysz lokalną zmienną o identycznej nazwie jak pole obiektu - tu powinien co najmniej polecieć warning
                                thrd1.start(); // odwołujesz się do zmiennej, która jest tutaj dostępna z tego miejsca w dwóch innych miejscach - powinien polecieć błąd
                        }

Zgaduję, że zamiast Thread thrd1 = new ... chciałeś napisać thrd1 = new ....
Co do drugiego problemu - dMovThrd to nie wątek - to tylko kod wykonywany w ramach wątku. Jeśli utworzysz dwa wątki wykonujące kod instancji klasy dMovThrd, to każdy z nich oddzielnie będzie wykonywać metodę run() tego obiektu. Oddzielnie i jednocześnie, bo to przecież osobne wątki w żaden sposób niesynchronizowane ze sobą. Oba wątki żyją jednocześnie z powodu wcześniej opisanego przeze mnie błędu.
Teraz wyobraź sobie wykonujący się jednocześnie w dwóch miejscach kod tej metody: kliknąłeś dwa razy w tym samym miejscu, a więc dwie jednocześnie wykonujące się metody run() przepychają Twój obrazek do danego punktu. Dwie naraz oznaczają, że współrzędne będą się zmieniać dwa razy szybciej - stąd to przyspieszenie.
Howgh.

0

A jeszcze z innej bajki - jeśli masz dwa wątki, jeden pisze a drugi czyta z tego samego obszaru pamięci, to może się okazać, że wątek czytający dostanie wymieszane ze sobą nowe i stare części danych, np. do wartość 15 będzie zmieniana na 240 i w teorii możliwe jest, że wątek czytający dostanie 0. Albo 255. To tylko taki przykład, bo akurat zapis do zmiennych tego typu (int, bool itp) z tego co pamiętam jest w java atomowy, jednak warto, żebyś wiedział czym to grozi w przypadku bardziej rozbudowanych zmiennych (np. Twoich obiektów modyfikowanych zamiast tych int'ów).

0

ŁF, thrd1 jest obiektem klasy Thread a nie polem, ale zwróciłeś mi uwagę na linijkę

Thread thrd1 = new ...
i to właśnie tutaj był błąd. Poprawiłem na
thrd1 = new...
i już działa tak jak powinno - działa tylko jeden wątek poruszający kulkę naraz . Dzięki za pomoc :)

PS. co do nazywania zmiennych to po prostu czasem ciężko osiągnąć kompromis między czytelnością i zwięzłością nazw. dMovThrd - to jednak faktycznie potworek

0
Qbisiek napisał(a):

ŁF, thrd1 jest obiektem klasy Thread a nie polem

Można być obiektem jakiejś klasy i równocześnie polem (field), właściwością (property) albo zmienną lokalną.

Qbisiek napisał(a):

co do nazywania zmiennych to po prostu czasem ciężko osiągnąć kompromis między czytelnością i zwięzłością nazw. dMovThrd - to jednak faktycznie potworek

Intellisense czy jak to się tam w java nazywa rozwiązuje problem długich nazw (i potworków też). Kod ma być przejrzysty i czytelny, a nazwy mają w tym pomagać, więc precz z potworkami :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1