2009-04-03 5 views
8

Also, ich habe zwei Threads.Ist der PrintWriter-Thread eines Java-Sockets sicher?

Thread eins verwaltet die Clientverbindungen. (Es gibt nur einen Client und einen Server)
Ich nenne es meinen Server-Thread.

Thread zwei verwaltet das Senden von Nachrichten an den Client. Ich nenne es meinen Nachrichtenprozessor-Thread.

Thread eins ist verantwortlich, unter anderem regelmäßig einen Herzschlag an den Client zu senden.

Bei der Programmierung nahm ich an, dass die Sockets nicht threadsicher waren, aber die Puffer waren, und solange ich separate Puffer für die Server- und Prozessor-Threads verwendete, würde es mir gut gehen.

Ich machte auch die Annahme, dass der "PrintWriter" analog zum Socket-Puffer in Java war.

Unter diesen Voraussetzungen habe ich diese Funktion einen Herzschlag senden:

public void sendHeartBeat(){ 
     logger.info("Sending a hearbeat!"); 
     PrintWriter printWriter=null; 
     try { 
      printWriter = new PrintWriter(clientSocket.getOutputStream()); 
     } catch (IOException e) { 
      logger.info(e.toString()); 
     } 
     if(printWriter!=null){ 
      printWriter.print("HEARTBEAT#"); 
      printWriter.flush(); 
     } 
    } 

Der andere Thread, der „Prozessor“ man tut etwas ähnlich, dass es funktioniert:

printWriter=new PrintWriter(theServer.getClientSocket().getOutputStream()); 

Auf diese Weise Ich würde jedes Mal einen neuen "Puffer" erstellen, wenn ich einen Herzschlag senden wollte, und meine Nachrichten würden niemals überschrieben werden.

Leider scheint dies nicht der Fall zu sein. Und ich bekomme eine Nachricht wie folgt durch die Leitung: dsgdsbHEARTBEAT # sdg

Dies verursacht einen Core Dump später.

Hier sind meine Fragen:

1) Sockel sind offensichtlich nicht Thread-sicher, sondern sind die PrintWriters ich von ihnen bekommen Thread sicher? Oder gibt es nur denselben PrintWriter zurück?

2) Was ist analog zum Socket-Puffer in Java? Wie sollte ich über dieses Problem nachdenken?

3) Wie mache ich es so, dass diese Threads nicht in den gleichen Puffer auf dem Socket schreiben?

Antwort

10

Es ist ein schlechtes Design, diese multiplen PrintWriter s im selben Stream zu haben. Wirklich wollen Sie mindestens das Objekt, das sie ruft, synchronisiert werden (oder Faden beschränkt).

jedoch unter der Annahme aus irgendeinem Grund mehrere PrintWriter s tun wollen:

Erstes Problem: Writer s nicht this als das Schloss nicht benutzen. PrintWriter und BufferedWriter standardmäßig verwenden beide die Writer sie sind mit als das Schloss konstruiert. Dies ist offensichtlich vollständig gebrochen. Sie sollten das Writer Schloss verwenden, nicht das Writer selbst.Ein einfacher Fehler, da das Sperren einer Funktion von Object die statische Sicherheit beseitigt. Sie müssen also eine PrintWriter mit dem Sockel OutputStream (oder einem anderen gewöhnlichen Objekt) als Schloss konstruieren.

Zweitens haben wir Pufferung innerhalb PrintWriter. So kommt das Ende eines Puffers, die Hälfte wird geschrieben und die Hälfte wartet auf den nächsten Schreibvorgang. Um dies zu verhindern, sperren Sie entweder extern print und flush oder verwenden Sie die automatische Spülung und fügen Sie ein neues Zeilenzeichen hinzu.

So ist es nicht sinnvoll Thread-Safe, aber Sie können es hacken. Oder Sie können ein besseres Design verwenden.

+0

Ich habe Java SE 1.6.0_31 src dafür angeschaut. 'PrintWriter (Writer out)' ruft schließlich 'protected Writer (Objektsperre)' auf, und dort weist 'Writer' diese 'Sperre' der internen 'Sperre' zu, die in 'Writer' zur Synchronisation verwendet wird. Könnten Sie pl. erkläre, warum du denkst, das Sperren ist hier kaputt? – shrini1000

+0

@ shrini1000 'PrintWriter' ruft' Writer (Objektsperre) 'mit' super (out); '. "Lock" ist also "out", anstatt "out.lock". (Dies ist ein Implementierungsproblem - die Spezifikation scheint zu fehlen.) –

+0

Dies scheint in Java 8 immer noch der Fall zu sein - PrintWriter ruft super (out) auf und lock ist out, statt out.lock. –

4

Sie benötigen eine Möglichkeit, das gleiche PrintWriter zwischen den Threads zu verwenden (t1.writer == t2.writer, nicht nur PrintWriter s aus demselben OutputStream erstellt). Mit dem gleichen PrintWriter sind alle Schreibvorgänge synchronisiert.

+1

Sie sagen, dass PrintWriter threadsicher sind, aber OutputStreams nicht? – Alex

+3

Ich denke, Chris sagt, dass PrintWriter Thread-sicher ist, aber zwei auf dem gleichen Stream sind nicht sinnvoll Thread-Safe. Deshalb benutze eins. –