# Java Multiuser Chat hohe Systemauslastung



## PrinzMartin (14. Jun 2009)

Moin Moin

ich programmiere momentan einen kleinen Multiuser Chat hauptsächlich um son bisschen in Java drin zu bleiben. Ich habs momentan folgendermaßen gelöst:

Server wartet auf verbindung
Client meldet sich
Server speichert verbindung und startet eine Unterklasse (extends Thread) welche die ganze zeit auf Nachrichten von diesem Client wartet.

Funktioniert auch soweit ganz gut. Das Problem ist, dass die Threads meine CPU auslastung direkt auf 100% katapultieren. Meine Frage ist jetzt wie man das niedrig halten kann. (mein Pidgin wartet ja beim Chatten auch dauernd auf nachrichten und die CPU auslastung ist ziemlich gering ich denke beinahe gleich null.

Gibt es eine Lösung in der man entweder darauf verzichten kann für jeden Client einen Thread zu starten der den InputStream abhört oder kann man irgendwie den Thread auf kleine flamme setzen?

ich hoffe ihr wisst hilfe 

Grüße
Martin


----------



## maki (14. Jun 2009)

Thread.yield()


----------



## Marco13 (14. Jun 2009)

Das könnte ich da jetzt nicht einordnen. Sollte ein Versuch, zu Lesen, nicht automatisch bliockieren solange nichts da ist?


----------



## PrinzMartin (14. Jun 2009)

Ging die Frage an mich?

Ich weiß nämlich nicht genau was du hören willst :-D. ich hab auch noch nix ausprobieren können, da ich gerade mit dem PrintWriter dass problem habe, dass leerzeichenscheinbar als trennung angesehen werden. wenn ich also schreibe "hallo welt wie gehts" wird das als 4 nachrichten interpretiert.

aber der versuch zu lesen blockiert ja. deshalb brauch ich ja die threads. ich seh nur, dass die Anzeige für meine cpu auslastung beinahe nichts anzeigt (ich chatte über icq, gucke fern, etc) wenn ich aber auch nur einen java thread starte der irgend etwas dauernd macht oder dauernd auf etwas wartet geht die direkt auf 100%

Grüße
Martin


----------



## madboy (14. Jun 2009)

Ähm... Du wartest aber nicht mit sowas?

```
while (true) {
  if(inputStream.read() != -1) {
    //irgendwas
  }
}
```


----------



## badmaxx (15. Jun 2009)

hab mich auch mal daran gemacht nen chat zu "bastel" 
hab mich so in den grundsätzen an diese tutorial gehalten.
habe auch das problem das meine cpu auslastung auf 100% steigt.
in dem beispiel quelltext wird über while(true) auf eine neue verbindung gewartet.
mir fällt aber im moment auch keine andere möglichkeit ein. hoff mir kann jemand vom schlauch runterhelfen.
Danke im Voraus


----------



## tuxedo (15. Jun 2009)

Naja, wenn ich das richtig gesehen habe, dann wäre das die schleife hier:


```
while(true)
		{
			Socket client=listen.accept();

			connection c = new connection(this, client);
			connections.addElement(c);
		}
```

Da "accept" solange blockiert bis eine neue, eingehende Verbindung kommt, liegt da nicht das Problem. 

Gibts da noch mehr solcher while-Loops? Wenn ja: Wie sehen die aus?

Tritt das Problem auch im Leerlauf (Server frisch gestartet, noch kein Client verbunden) auf?

P.S. Kannst ja auch mal in jede while-Loop ein "System.out.println("While XYZ");" einbauen. Dann siehst du welche Schleife sich zu tode läuft ... Alternative halt nen Debugger drauf ansetzen.


----------



## badmaxx (15. Jun 2009)

so danke schonmal für die antwort.
hab jetz mal n System.out.println("...") in jede loop gepackt und festgestellt das ich keine endlos schleife hab.
Hab auch festgestellt das wenn ich den Server starte und n clients sich verbinden besteht kein problem.
Die CPU-Auslastung steigt erst wenn sich clients abmelden.
Hängt des evtl. damit zusammen das thread.stop(); deprecaded ist?
Irgendwie raff ich des nich was ich machen muss das ich den blöden thread stoppen darf.


----------



## Gast2 (15. Jun 2009)

badmaxx hat gesagt.:


> Irgendwie raff ich des nich was ich machen muss das ich den blöden thread stoppen darf.


Selbstmord der Threads


```
private boolean ende = false;
public void ClientThread()
{
    syso("Client erfolgreich gestartet");
    while(!ende)
    {
        // ...
    }
    // ...
    syso("Client hat sich verabschiedet");
}
public void Quit()
{
    ende = true;
    // ...
}
```

hand, mogel


----------



## badmaxx (15. Jun 2009)

des muss doch noch was einfacheres geben!
ich glaub einfach nich das ich mehr als 2 zeilen brauch um nen thread zu stoppen


----------



## tuxedo (15. Jun 2009)

Das ist das einfachste und zuverlässigste und nebenbei auch gängige Praxis. 
Wobei: Die Loop von Mogel liegt im Konstruktur und nicht in der run() Methode... Wäre also so nicht ganz korrekt. Aber das Prinzip ist und bleibt das gleiche.


- Alex


----------



## PrinzMartin (15. Jun 2009)

oha so viele antworten :-D

also im leerlauf ohne verbundenen Client gehts auch schon hoch weil halt beim Server die besagte "accept" whileschleife die ganze zeit blockiert und wartet. Außerdem warte ich auch auf eingehende Nachrichten mit genau so einer while schleife. Kann man das denn irgendwie besser machen?


----------



## tuxedo (15. Jun 2009)

accept() blockiert. Das ist richtig. D.h. aber auch, dass die Schleife nicht mit 100% CPU Leistung sich im kreis dreht. Es wird gewartet bis ein Client kommt. Erst dann geht die Loop in die nächste Runde. Und das kostet (fast) keine CPU Zeit.

Poste doch mal deinen kompletten Servercode... (wenns nicht allzuviel ist). Dann kann mans direkt an der eigenen Maschine ausprobieren.

- Alex


----------



## Gast2 (15. Jun 2009)

tuxedo hat gesagt.:


> Wobei: Die Loop von Mogel liegt im Konstruktur und nicht in der run() Methode... Wäre also so nicht ganz korrekt.



???:L


```
new Thread( new Runnable() { public void run() { ClientThread(); } }).start();
```


----------



## tuxedo (15. Jun 2009)

Klar, so geht's auch. Ist doch aber nicht der gängige Weg, oder? Soll jetzt auch nicht Diskussionsgegenstand werden... Ging ja an sich um die Abbruchbedingung.

- Alex


----------



## PrinzMartin (16. Jun 2009)

Ah ich hab die endlosschleife gefunden.

while(!in.ready()) {}

wenn ich da ein Thread.sleep(1); reinsetze dann gehts. ich hab noch nich ganz kapiert wozu das gut ist aber man scheint es zu brauchen da ansonsten der server spinnt sobald ein client beendet wird.

Grüße
Martin


----------



## faetzminator (16. Jun 2009)

Mach mal statt ein Thread.sleep(1) ein Thread.yield()


----------



## PrinzMartin (16. Jun 2009)

mhh mit Thread.sleep(1)  hab ich keine CPU auslastung mit Thread.Yield() schießt die direkt hoch.

die stelle um die es geht sieht insgesamt im server so aus



```
public void run()
    {
      
      try {
      boolean fertig = true;
      in = new BufferedReader(new InputStreamReader(skt.getInputStream()));
      //out = new PrintWriter(skt.getOutputStream(), true);

      while(fertig)
      { 
        Thread.sleep(1); 
        
        while (!in.ready()) {Thread.yield();}
        
        data = in.readLine(); 
        System.out.print("Empfangen vom Client:   " + data);
        senden_an = hole_liste.getSktListe();
        senden_an.toFirst();
        while(!senden_an.isBehind())
        {
          skt = (Socket)senden_an.getItem();
          out = new PrintWriter(skt.getOutputStream(), true);
          out.print(data);
          out.println();
          senden_an.next();
        }
        
        System.out.println();
        System.out.print("Gesendet zum  Client:   " + data);
        System.out.println();
       
        //out.flush();

      }
      
      }
      catch(Exception e) {
         System.out.print("Fehler in cS_receive_thread    "+e);
         }	  	
    }
```


und der Client:

```
public void run()
    {
     try {
      boolean fertig = true;
      while(fertig)
      { 
        
        
        while (!in.ready()) {Thread.yield();}
        data = in.readLine();
        jTa_Output.setText(jTa_Output.getText()+data+"\n"); 
        //System.out.print("Empfangen vom Server:   "+data);
        //System.out.println();
       
        }

      //in.close();
      }
      catch(Exception e) {
         System.out.print("Fehler in cC_receive_thread     "+e);
      }	  	
    }
```



Grüße
Martin


----------



## tuxedo (16. Jun 2009)

Meines erachtens ist die Implementierung und Abbruchbedingung mit "in.ready()" so nicht ganz korrekt.. 

Schau mal in die Doku zu readLine() ... Da steht, dass wenn "null" zurück kommt, das Ende des Streams erreicht ist. DAS solltest du also abfragen: "while(data!=null) {...}".

- Alex


----------



## PrinzMartin (16. Jun 2009)

also das basiert zum größten teil auf einem beispiel alles bin ich noch nicht durchgegangen.

deine Theorie bestätig, dass der server ohne dieses while die ganze zeit "Null" empfängt und sendet sobald sich der client verabschiedet.

mhh klappt nicht. irgendwie empfängt der client dann nix mehr aber warum eigentlich nicht in.ready() laut doko:

public boolean ready()
Liefert true, wenn Daten zum Lesen aus dem Stream bereitstehen, so dass ein nachfolgender Aufruf von read() nicht blockiert, sonst false.

theoretisch müsste man das also einfach komplett weglassen können. Is mir ja egal ob das blockiert oder nicht. Dafür hab ichs ja in nen Thread gepackt

ich teste mal.


----------



## Gast2 (16. Jun 2009)

der Server müsste bei readLine() in den blockierenden Modus übergehen ... also dort stehen bleiben bis etwas ankommt


----------



## tuxedo (16. Jun 2009)

Eben. Wenn "null" gelesen wird ist der Stream geschlossen, die Leitung tot und der Thread kann beendet werden. Und wenn ein Stream geschlossen ist, dann sollte readLine() auch nicht mehr blockieren ... 

btw: Nicht alle Tutorials "draußen im Netz" machen's "richtig". Viele machen's "just make it work... somehow...". Also immer nochmal drüber schauen und bei unbekannten Methoden mal die JavaDoc konsultieren. Ist zwar Arbeit, aber man lernt doch einiges daraus.

- Alex


----------



## badmaxx (16. Jun 2009)

so hab mein problem zwar noch nich ganz gelöst und der werd ich auch nich mehr schaffen aber ich bin schon schlauer!
es hängt anscheinend mit dem appletviewer von java zusammen.
denn wenn ich das ganze in eine html datei einbinde funktioniert es ohne probleme!
wenn ich den viewer über den stoppbutton der IDE schließe gibt es auch keine problem nur wenn ich das ganze über das X in der ecke schließe spinnt er!

allerdings hab ich jetz ein anderes problem.
Ich will eine userliste einbinden was soweit ja kein problem ist, allerdings füllt er mir wenns im html eingebunden ist die liste nichtmal mit meinem eigenen namen(was er im viewer tut), dort fügt er aber auch nur einen namen ein! ich habe das gefühl das er für jeden thread eine neue List anlegt.

Server:

```
public static List users = new List();

public static List getUsers()
  {
    return users;
  }

public static void setUsers(List users)
  {
    chatserver.users = users;
  }
```

Client:

```
users = chatserver.users;
users.add("test");
chatserver.setUsers(users);
```

hab ich da irgendwo nen denkfehler drin?


----------



## Gast2 (16. Jun 2009)

```
// public static List users = new List();
private static List users = new List(); // private - weil Member

public static List getUsers() // Zugriff auf Member nur via Getter & Setter
  {
    return users;
  }

// unnötig
// public static void setUsers(List users)
//  {
//    chatserver.users = users;
//  }
```


```
// users = chatserver.users;
// users.add("test");
// chatserver.setUsers(users);
ChatServer-Klasse.getUsers().add("test")
```
auf statische Methoden greift man über den Klassennamen zu ... nie über Instanzen

hand, mogel


----------



## badmaxx (17. Jun 2009)

hab das ganze grad mal ausprobiert und irgendwie will des aber immer noch nicht.
also entweder war mein urlaub zu lang oder zu kurz


----------

