Problem z Pętlą

0

Witam,
Co z tą pętlą jest nie tak ze nie chce się wykonywać ?

Wszystko się kompiluje niby dobrze, 0 error-ów.

while(true){
	if(listen != null ){
	strListenerArray = listen;
	listenerArray = strListenerArray.split("#%?");
	cO = listenerArray[1];
	cC = listenerArray[2];
	Name = listenerArray[3];
	posX = listenerArray[4];
	posY = listenerArray[5];
		if(listenerArray[0] == adr){
			playerInfo.set(0,cO);
			playerInfo.set(1,cC);
			playerInfo.set(2,Name);
			playerInfo.set(3,posX);
			playerInfo.set(4,posY);
			System.out.println(playerInfo);
		}
	}
}

oczekuje na spam System.out.println(playerInfo); w konsoli a tu nic ;/
Co do wartości czy się zgadzają to jestem pewien że tak.

0

Nie będziesz miał żadnego spamu bo jeżeli chcesz się upewniać czy zgadłeś adres wytworzonego przez split na stercie pierwszego Stringa, to musisz mieć cholerne szczęście, żeby zobaczyć coś na wyjściu. :)

0

Aha, a co do tego ma szczęście :D ?, a tak wgl to jak to mogę inaczej zrobić ?

0

Szczęście jest tu kluczowe ponieważ listenerArray[0] dostaje obiekt wytworzony gdzieś na stercie (za wyjątkiem przypadku użycia intern() jego adres jest zawsze inny i unikalny). To co robisz, to porównujesz czy Twój adr ma tę samą referencję co ten obiekt. Jest tylko jeden taki przypadek gdy ten warunek może być spełniony - kiedy listenerArray[0] dostanie nulla, a adr też będzie nullem. A to z kolei jest możliwe tylko wtedy kiedy równolegle dostaniesz OutOfMemoryError. Dość rzadki przypadek. :)
Krótko mówiąc Twój warunek prawie nigdy nie będzie spełniony bo nie ma możliwości aby dwa niezależnie stworzone obiekty miały tę samą referencję. A nad tą stworzoną przez split nie masz żadnej kontroli.

Jeżeli adr jest stringiem, to zastąp "== adr" przez ".equals(adr)". Wtedy będziesz przynajmniej porównywać teksty. Jeżeli taka była intencja.

0

Zrobiłem tak jak mówiłeś zastosowałem .equals, ale niestety dalej to samo, lipa nie zapętla się i nie otrzymuje spamu w konsolce mimo że wartość wynosi true gdy odciąłem if'a z warunkiem za pomoca /* */ i dałem za to System.out.println(adrr.equals(adr))

Tutaj kod:

while(true){
        if(listen != null ){
                strListenerArray = listen;
                listenerArray = strListenerArray.split("#%?");
                adrr = listenerArray[0];
                cO = listenerArray[1];
                cC = listenerArray[2];
                Name = listenerArray[3];
                posX = listenerArray[4];
                posY = listenerArray[5];
                if(adrr.equals(adr)){
                        playerInfo.set(0,cO);
                        playerInfo.set(1,cC);
                        playerInfo.set(2,Name);
                        playerInfo.set(3,posX);
                        playerInfo.set(4,posY);
                        System.out.println(playerInfo);
                }
        }
}

Ale mam też drugi problem(rozwiązany na swój sposób...) którego nie wiem jak wytłumaczyć.

while(true){
        valRequests = fakeSocketContainer.size();
        System.out.println(valRequests+"--"+enableTimer); // tu tkwi problem
        if(valRequests > 0 && enableTimer == true){
                fakeSocketContainer.remove(0);
                delaySendSocket = new Timer();
                delaySendSocket.schedule(new sendSocketToClients(), 190 );
                enableTimer = false;
        }
}

Ta pętla też mi się nie chciała zapętlić i na rozwiązanie wpadłem przez przypadek gdy w celu sprawdzenia dodałem System.out.println(valRequests+"--"+enableTimer); po to sprawdzić o co biega i tu nagle działa i uruchamia timera który wysyła do klienta informacje.

0

Nadal nie mam pojęcia czym jest adr lub adrr i skąd dostaje wartość.

Wywal te warunki i sprawdź co się dzieje. Jak nic Ci nie wypisuje, to te warunki albo nigdy nie są spełnione, albo w ogóle sterowanie do niej nie dochodzi.
No i brak wyjścia z pętli nieskończonej (gdzieś w bloku) jest podstawowym błędem opisywanym we wszystkich podręcznikach jakie znam.
Użyj głowy. Już Einstein powiedział, że "ludzie są naiwni gdy robiąc to samo oczekują za każdym razem innych wyników". A każdy komputer przestrzega tej zasady do bólu. Z definicji.

ps. Daj sobie więcej informacji. Wrzuć więcej println-ów w różne miejsca. To już nawet Ci z NASA więcej będą wiedzieć od Ciebie kiedy im się łazik rozkraczy niż ty z tego kodu.

0

Chodzi o to że gdy klient się podłączy dostaje osobny wątek i w adrr jest przechowywany jego adres, poza tym przechowywane są informacje o Name i Pozycji.A adr jest to adres klienta który właśnie nadaje.
If jest po to aby sprawdzić czy wiadomość która jest dostarczona na serwer jest właśnie dla tego wątku(klienta) jak tak to aktualizuje jego pozycje i name.

Co do pętli to w sumie nie chce aby ona miała zakończenie chce aby cały czas sprawdzała.

PS. adres pobieram z socket.getRemoteSocketAddress(). A i wywaliłem warunki to System.out.println("test"); poprawnie mi wyświetlało więc to wyglądało tak jakby na problem ze spełnianiem warunków.

EDIT:
Problem częściowo rozwiązany jak ten drugi problem co wyżej wymieniłem, na początek pętli po prostu dodałem System.out.println(listen!=null);
No i otrzymuje w konsole spam na przemian z
System.out.println(listen!=null) oraz
System.out.println(playerInfo);

Tak jak chciałem, tyle że bez tego pierwszego.
Problem wygląda na rozwiązany ale nie potrafię sobie powiedzieć dlaczego to działa...

while(true){
	System.out.println(listen!=null);
	if(listen != null ){
		strListenerArray = listen;
		listenerArray = strListenerArray.split("#%");
		adrr = listenerArray[0];
		cO = listenerArray[1];
		cC = listenerArray[2];
		Name = listenerArray[3];
		posX = listenerArray[4];
		posY = listenerArray[5];
		if(adrr.equals(adr)){
			playerInfo.set(0,cO);
			playerInfo.set(1,cC);
			playerInfo.set(2,Name);
			playerInfo.set(3,posX);
			playerInfo.set(4,posY);
			System.out.println(playerInfo);
		}
	}
}
1
lukaszml napisał(a):

Chodzi o to że gdy klient się podłączy dostaje osobny wątek

I to jest właśnie kluczowe. Wątek oznacza, że różne wątki dobijają się do tych samych zmiennych. A to oznacza kolizje, uszkodzenia danych itp.
Po prostu niczego nie synchronizujesz.

i w adrr jest przechowywany jego adres

Nie. W adrr jest przechowywany jakiś string odcięty przez znaki #%? z początku zmiennej listen, która rzekomo powinna być stringiem. A czy jest, to powinieneś się dowiedzieć.

A adr jest to adres klienta który właśnie nadaje.

Zakładam, że rozumiesz pod tym ciąg znaków reprezentujący adres IPv4 znany potocznie jako IP.
Czy w ogóle jesteś pewien, że addr nie może mieć absolutnie żadnej spacji czy innego znakowego śmiecia? Bo jeżeli porównujesz przez equals, to ob stringi muszą być absolutnie identyczne. Co do bita. No i mieć oczywiście ten sam typ String. Jeżeli masz coś innego co realizuje interfejs CharSequence (np. CharBuffer), to też odpada bo porównanie zawsze zwróci false.

If jest po to aby sprawdzić czy wiadomość która jest dostarczona na serwer jest właśnie dla tego wątku(klienta) jak tak to aktualizuje jego pozycje i name.
Co do pętli to w sumie nie chce aby ona miała zakończenie chce aby cały czas sprawdzała.

To jak kończy się ta aplikacja, która uruchamia tę pętlę? Dostaje kopa w dupę z innego wątku za pomocą System.exit? Czy po prostu dostaje w końcu killa od systemu? :)

PS. adres pobieram z socket.getRemoteSocketAddress(). A i wywaliłem warunki to System.out.println("test"); poprawnie mi wyświetlało więc to wyglądało tak jakby na problem ze spełnianiem warunków.

Twoim przyjacielem Debugger jest. A jest on wbudowany zarówno w Eclipse, Netbeans i kupę innych mniej lub bardziej darmowych IDE. Szczególnie jak chcesz obsługiwać w nim radio/sieć czy inne niedeterministyczne wynalazki, to jest on bardziej niż niezbędny.

Problem częściowo rozwiązany jak ten drugi problem co wyżej wymieniłem, na początek pętli po prostu dodałem System.out.println(listen!=null);

Nie. To nie jest wcale rozwiązane. To co robisz, to niejawnie wymuszasz taką tyłkową synchronizację za pomocą bufora System.out.
Jeżeli już chcesz ryzykować to oznaczaj przynajmniej pola przez modyfikator volatile, albo (ewentualnie) użyj typu AtomicInteger lub podobnego. Po prostu poczytaj o współbieżności. Wszystkie metody, przez które ma dostęp więcej niż jeden wątek (nawet tylko do odczytu) musisz synchronizować dla każdego jednego pola dowolnego obiektu. Z tego co widzę, to zabierasz się za współbieżność i komunikację nie znając nawet podstaw posługiwania się nimi.

Problem wygląda na rozwiązany ale nie potrafię sobie powiedzieć dlaczego to działa...

I to jest Twoje zadanie domowe. :)
Jako podpowiedź masz tajemne słowa synchronize, volatile oraz bardzo ewentualnie AtomicInteger. Poczytaj o współbieżności bo bez tego nie zrobisz żadnej sensownej komunikacji. Chyba, że chcesz zatrzymywać program na czas transmisji klient-serwer.

0

No muszę sprawdzić dokładnie te wątki bo między nimi przechodzi kilka jednakowych zmiennych i może to jest problemem.
Adrr to adres który jest zapisany w postaci String :).
Tak jest to ciąg znaków które konwertowane są do String po czym Adr porównuje sie z Adrr(ale nie w tym problem bo tu działa)
Szczerze pewnie killa od systemu ja nie uwzględniłem jej zamknięcia :)

Tak masz racje nie znam podstaw współbieżności oraz komunikacji między socketami, tak naprawdę korzystam z gotowego serwera Chat którego chce przerobić na Game Serwer który zbiera informacje o graczu zapisuje je w zmiennych a potem aktualizuje jej pozycje itp do Array List i wysyła z powrotem do wszystkich klientów(Flash).

Ahh chyba za szybko poleciałem, do tego nie znając podstaw wyżej ów wymienionych.

Hah no to mam zagadkę a raczej zadanie na wieczór :D

Czyli radzisz mi abym pierw poduczył się podstaw komunikacji i współbieżności a potem się za to zabierał? bo szczerze "volatile oraz bardzo ewentualnie AtomicInteger" nic z tego nie zrozumiałem :D

1

Najkrótszy na świecie kurs współbieżności

  1. Wątki
    Java pozwala tworzyć nowe wątki, przez utworzenie obiektu Thread. Wątki w Javie są od pewnego czasu natywne, czyli JVM deleguje je na system. Dzięki temu system może przerzucać ich wykonanie między fizycznymi procesorami/rdzeniami nawet w trakcie ich działania. Dzięki temu system może równomiernie rozłożyć wiele wątków między wszystkie procesory/rdzenie. Utworzenie/zniszczenie wątku jest kosztowne czasowo. Wszystkie wątki na jednym cpu dzielą między sobą czas wykonania, a przełączanie między nimi też jest kosztowne czasowo. Przy dużej liczbie wątków na jeden cpu czas przełączania może być porównywalny z czasem wykonywania, dlatego każda aplikacja powinna ograniczać liczbę równolegle działających wątków do rzędu wielkości liczby procesorów.

  2. Operacje
    Jeżeli jakiś kod uruchomiony z co najmniej dwóch wątków robi jakieś operacje na polu, to może się zdarzyć, że odczyt lub zapis może się wykonać z obu wątków "jednocześnie". Jeżeli operacje te nie są atomowe, czyli niepodzielne na poziomie procesora, to jakiś odczyt może nastąpić w trakcie operacji zapisu, wtedy następuje awaria danych - najgorsze paskudztwo do debugowania bo operacja przestaje być deterministyczna (czytaj zaczyna być losowa).
    Wszystkie zapisy i odczyty zmiennych maks. 32-bitowych (char, byte, short, int, float, referencja 32-bit) są operacjami atomowymi. Inkrementacja i dekrementacja nie są atomowe, reszta operacji też nie jest, ale mogą (zależnie od JVM).

Mimo, że operacje mogą być atomowe, to ze względu na budowę komputera, cache CPU, systemu i JVM ich nowe wartości mogą nie być od razu (lub w ogóle) widziane dla innych wątków. Odczyt w innym wątku może dać wcześniejszą wartość widzianą w tym wątku.
Żeby ten problem pominąć można za niewielką cenę szybkości kodu spowodować widoczność wartości zmiennej dla wszystkich wątków przez użycie modyfikatora volatile. Dla operacji atomowych to zwykle rozwiązuje sprawę. Dla operacji nieatomowych lub całego ciągu operacji stosuje się synchronizację.

Użycie konstrukcji synchronized(obiekt) {...} pozwala przywrócić z punktu widzenia wątku atomowość instrukcji znajdujących się w bloku za sporą cenę szybkości kodu. Pod warunkiem, że dostęp do pola będzie możliwy wyłącznie z kawałków kodu synchronizowanego. Dlatego tak istotna jest enkapsulacja prywatnych danych w obiekcie - dzięki temu można zsynchronizować samego settera i gettera i problem synchronizacji ma się z głowy. Napis obiekt w bloku synchronized oznacza dowolny obiekt, który będzie sobie trzymał identyfikację blokady. Dla każdego dostępu musi to być zawsze ten sam obiekt, blokady na różnych obiektach nie będą skuteczne i może się pojawić awaria danych. Jeżeli metoda ma modyfikator synchronized, to oznacza to, że cały jej blok jest synchronizowany i to obiektem this, czyli zazwyczaj tym, który posiada pola, które mają mieć dostęp synchronizowany. Użycie synchronizacji powoduje z automatu widoczność, więc w takim wypadku należy pozdejmować modyfikatory volatile na tych zmiennych (będą powodować tylko zbędny narzut czasowy). Operacje wewnątrz bloku synchronizowanego są krytyczne - im jest ich mniej i są krótsze tym lepiej. Nigdy nie należy wywoływać z sekcji krytycznych żadnych metod polimorficznych (public, protected), ani cudzego kodu (wywołań z kodu klienta) bo złamie to sekcję krytyczną i synchronizacja pójdzie się bujać lub pojawią się zakleszczenia. Dlatego synchronizowany getter i setter dla prostego pola prywatnego, to ideał synchronizacji.
Synchronizacja działa w ten sposób, że jeżeli wykonywane są instrukcje bloku synchronizowanego przez jeden wątek, to drugi musi czekać aż ten pierwszy skończy. Działa to jak semafor dla pociągów próbujących wjechać na wspólny tor. Na raz może tylko jeden dlatego nigdy nie pojawi się kolizja.

Synchronizacja modyfikowalnych pól obiektowych jest trudniejsza ponieważ dostęp do całego obiektu jest złożony z wielu operacji więc nie może być on nigdy atomowy - synchronizacja jest jedyną możliwością. Na dodatek jedynym sposobem zapewnienia synchronizacji przy przekazywaniu wartości takiego obiektu jest przekazanie jego głębokiej kopii (którą należy wykonać właśnie w sekcji krytycznej). Jedynym wyjątkiem od tej zasady są obiekty niezmienne (immutable), które można swobodnie przekazywać bez synchronizacji (wystarczy volatile na referencji).

Oprócz tego istnieją jeszcze obiekty synchronizujące takie jak Lock, które zastępują bloki synchronized i w dużej skali są wydajniejsze oraz Semaphore (->javadoc).

Alternatywą do synchronizacji jest użycie atomowych typów danych. Są one synchronizowane z automatu ze względu na specyficzną listę operacji i typ dostępu do danych i są w porównaniu do volatile bardzo szybkie, tym bardziej wobec synchronizacji. Jest kilka typów atomowych AtomicBoolean, AtomicInteger, AtomicLong*, AtomicReference* oraz tablice AtomicArray ( = nic lub różne/specyficzne typy -> javadoc). Trzy ostatnie mają kilka podtypów o różnych zachowaniach i typach operacji atomowych (innych nie mają). Nie trzeba ich synchronizować, ani (prawdopodobnie) używać volatile. Ceną ich użycia jest bardzo ograniczona liczba bardzo specyficznych operacji takich jak np. warunkowy zapis.

Najmniejszą cegiełką synchronizacji są operacje wait i notify/notifyAll. Działają one wyłącznie w sekcjach krytycznych, czyli w blokach synchronizowanych. Operują na nich wszystkie metody i operacje współbieżności z bibliotek Javy, więc ich ręczne używanie jest w normalnym kodzie właściwie zbędne (chyba, że ktoś chce wynajdować nową wersję koła). Operacje te są zastępowane przez wysokopoziomowe kolejki interfejsu BlockingQueue: LinkedBlockingQueue i ArrayBlockingQueue.

Operacje klasy Thread: suspend, resume, stop i destroy są przestarzałe ponieważ próbują zatrzymać i wznowić wykonanie kodu również w trakcie operacji nieatomowych, a na dodatek stan danych, na których operuje wątek staje się nieokreślony (jedne mogłyby być zaktualizowane, inne nie). Aby temu zaradzić zapętlony kod wątku powinien być ręcznie przerywany (np. warunkiem pętli lub break) po wykryciu stanu żądania jego zapauzowania/zatrzymania lub przez użycie metody Thread.isInterrupted(). Metoda Thread.yield() jest koncepcyjnie przestarzała i służy do wymuszania wielowątkowości na starych systemach bez wywłaszczania. Na systemach z wywłaszczaniem wątków może mieć lub nie mieć żadnego skutku.

Jeżeli dostęp do jednej danej synchronizowanej wymaga dostępu do danej synchronizowanej innego obiektu, a ta wymaga dostępu do pierwszej danej, to pojawia się cykl, który prowadzi do zakleszczenia programu. Cykl może składać się z większej ilości elementów niż 2.

  1. Przydatne klasy narzędziowe do współbieżności
    CountDownLatch - wielowątkowy licznik wykonania zatrzymujący wątki do momentu wyzerowania licznika; pozwala zatrzymać i wznowić różne wątki w tym samym momencie.
    CyclicBarrier - j.w., ale zatrzymuje wątki bez licznika i jest do wielokrotnego użycia

Reszta ->javadoc (Executors, Executor/ExecutorService, Runnable, Callable, Future, Executors: newSingleThreadExecutor, newFixedThreadPool, newScheduledThreadPool, newCachedThreadPool() i osobno new ForkJoinPool() z jego zadaniami RecursiveTask i RecursiveAction).

0

Ah Olamagato ale ty mi ułatwiasz życie :), dzieki.
Problem(zadanie) rozwiązany.

Dla kogoś z tym samym problemem to zapraszam do lektury UP^, ale podpowiem że w moim przypadku wystarczyło dodać volatile do zmiennych

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