2017-02-27 5 views
1

Ich habe dieses Problem in Java. Ich habe eine Server-Klasse namens MyServer und ich möchte einen Thread-Pool implementieren, wo jeder Thread eine Methode von MyServer ausführt, wenn eine Anfrage kommt. Ich habe eine andere Klasse erstellt, die einen Serverpool namens MultiThreadedSocketServer implementiert. Die Klasse ist dies:Konkurenz in einem Thread-Pool in Java

public class MultiThreadedSocketServer { 

public void startServer(MyServer s, int localport, int threadPoolSize) { 
    final ExecutorService clientProcessingPool = Executors.newFixedThreadPool(threadPoolSize); 

    Runnable serverTask = new Runnable() { 
     @Override 
     public void run() { 
      try { 
       ServerSocket serverSocket = new ServerSocket(localport); 
       System.out.println("Waiting for clients to connect..."); 

       while (true) { 
        Socket clientSocket = serverSocket.accept(); 
        clientProcessingPool.submit(new ClientTask(clientSocket, s)); 
       } 
      } catch (IOException e) { 
       System.err.println("Unable to process client request"); 
       e.printStackTrace(); 
      } 
     } 
    }; 
    Thread serverThread = new Thread(serverTask); 
    serverThread.start(); 
} 
} 

die Klasse mit dem Namen MultiThreadedSocketServer hat ein Argument Server s benannt, die es in Client-Task-Klasse übergibt, die ein Thread erstellt wird. Die Client-Task-Klasse ist dies:

class ClientTask implements Runnable { 
    private final Socket clientSocket; 
    private MyServer s; 

    public ClientTask(Socket clientSocket, MyServer s) { 
     this.s = s; 
     this.clientSocket = clientSocket; 
    } 

    @Override 
    public void run() { 
     System.out.println("Got a client !"); 

     String inputLine = null; 
     try { 

     BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
     // Do whatever required to process the client's request 
     inputLine = in.readLine();   

     if (inputLine.equals("Bye")) { 
      System.out.println("Bye"); 
      System.exit(0); 
     } 

     s.handleRequest(inputLine); 

      clientSocket.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Wie Sie sehen können, wenn eine Anforderung der handle Methode der Klasse MyServer kommt aufgerufen. Ich möchte, dass diese Methode synchron ausgeführt wird, dh immer nur ein Thread, um diese Methode ausführen zu können. Das Hinzufügen von synchronisiert vor der Methodenimplementierung bringt nichts.

Kann jemand mir den richtigen Weg geben, dies zu tun? Vielen Dank im Voraus für Ihre Zeit.

PS: Ich habe den ganzen Code

MyServer Klasse http://pastebin.com/6i2bn5jj

Multithreaded Server-Klasse http://pastebin.com/hzfLJbCS

Wie es in Haupt ersichtlich schaffe ich drei Anfragen mit handle mit Argumenten Aufgabe, task2 und Tschüss.

Das würde eine korrekte Ausgabe

Waiting for clients to connect... 
Got a client ! 
This is an input Task 
Request for Task 
Got a client ! 
This is an input task2 
Request for task2 
Got a client ! 
This is an input 
Bye 

werden, sondern ist die Reihenfolge gemischt. Manchmal kann Bye, der den Server schließt, zuerst ausgeführt werden. Ich möchte sicherstellen, dass die Bestellung diejenige ist, in der die Anfragen hauptsächlich erstellt werden.

+0

Wenn 'MyServer.HandleReq() 'synchronisiert wird, verhindert dies, dass mehrere Threads die Methode gleichzeitig ** auf derselben MyServer-Instanz ** aufrufen. Wie viele MyServer-Instanzen haben Sie? Randnotiz: Bitte beachten Sie die Namenskonventionen. –

+0

Nur eine MyServer-Instanz wird immer ausgeführt. Entschuldigung, ich weiß nicht über die Namenskonventionen, wenn Sie klarstellen möchten, dass ich meinen Beitrag bearbeiten werde –

+1

Methoden beginnen mit einem Kleinbuchstaben. HandleReq sollte HandleReq oder besser HandleRequest sein. Gleich für Variablen. ThreadPoolSize sollte threadPoolSize sein. Wenn Sie eine einzelne MyServer-Instanz haben, ist die Synchronisierung der Methode in Ordnung. Wie testen Sie, dass es nicht richtig synchronisiert ist? –

Antwort

2

Aber stattdessen ist die Reihenfolge gemischt. Manchmal kann Bye, der den Server schließt, zuerst ausgeführt werden. Ich möchte sicherstellen, dass die Bestellung diejenige ist, in der die Anfragen hauptsächlich erstellt werden.

Sie sagen, dass Sie möchten, dass der Server Anforderungen in Reihenfolge verarbeitet. Dies ist schwer zu gewährleisten, da Sie 3 Sockets öffnen und sie auf den Server schreiben, ohne auf eine Antwort zu warten. Dies ist implementierungsabhängig, aber ich bin nicht sicher, ob es eine Garantie gibt, dass, wenn der Client von einem Socket InputStream schreibt, der Server die Bytes erhalten hat. Dies bedeutet, dass vom Client aus keine Garantie dafür besteht, dass das IO in der von Ihnen gewünschten Reihenfolge ausgeführt wird.

Um zu sehen, ob dies das Problem ist, würde ich die System.exit(0) entfernen, um zu sehen, ob die anderen Zeilen es machen, direkt nach der "Bye" Zeichenfolge. Oder Sie könnten eine Thread.sleep(5000); vor der exit(0) setzen.

Eine einfache Art der Reparatur wäre, um sicherzustellen, dass Ihre PrintStream Auto-Flush eingeschaltet ist. Das wird zumindest den Socket aufrufen, aber selbst dann gibt es Race Conditions zwischen dem Client und dem Server. Wenn die automatische Spülung nicht funktioniert, muss der Client auf eine Antwort vom Server warten. Also würde der erste Client den ersten Befehl schreiben und auf die Bestätigung warten, bevor er zum zweiten Befehl geht.

In Bezug auf Ihre ursprüngliche Frage, Sperren auf dem Server würde nicht wegen der Race-Bedingungen helfen. Die "Bye" macht es vielleicht zuerst und sperrt den Server gut.

Diese Art von Fragen rund um die Synchronisierung der Threads in einem Multithread-Programm machen wirklich keinen Sinn für mich. Der gesamte Punkt der Threads ist, dass sie asynchron parallel laufen und nicht in einer bestimmten Reihenfolge arbeiten müssen. Je mehr Sie Ihr Programm zwingen, die Ausgabe in einer bestimmten Reihenfolge auszuspucken, desto mehr streiten Sie sich dafür, dies ohne irgendwelche Threads zu schreiben.

Hoffe, das hilft.

0

Wenn das Problem darin besteht, dass die Nachricht bye den Server tötet, bevor andere Anfragen bearbeitet werden können, könnte eine Lösung sein, System.exit(0); unter bye nicht anzurufen. Die Nachricht könnte einen Merker blockieren weitere Anfragen von behandelt werden und auch einige andere Mechanismus zu benachrichtigen, System.exit(0); aufrufen, wenn der Thread-Pool im Leerlauf ist, ohne Anfragen noch zu behandeln.