Heim  >  Artikel  >  Java  >  Memo für Anfänger zum Erlernen von Java (9)

Memo für Anfänger zum Erlernen von Java (9)

黄舟
黄舟Original
2016-12-20 13:55:23997Durchsuche


Ich habe plötzlich den Unterschied zwischen der Verwendung von Schnittstellen zum Implementieren von Multithreading und der Verwendung von Klassen zum Erstellen von Thread-Körpern entdeckt. Bei der Verwendung von Klassen zum Erstellen von Thread-Körpern schien es mir nicht wichtig zu sein Um diese Klasse zum Definieren von Threads zu verwenden, z. B. MyThread thread1=New MyThread(), und wenn Sie die Schnittstelle zum Erstellen eines Thread-Körpers verwenden, müssen Sie nur die Thread-Klasse verwenden, z. B. Thread thread1=new Thread(MyThread). . Dies ist das vor einigen Tagen erwähnte Multithreading, das sich im Socket widerspiegelt.

Die unermüdlichen Bemühungen in der Vergangenheit haben es mir ermöglicht, die ursprüngliche unvollendete Arbeit fortzusetzen und offiziell auf die Connection-Pool-Version von Socket umzusteigen.

Überprüfen Sie, wie wir den Multithread-Server MultiThreadRemoteFileServer erstellt haben. Zusammenfassend lässt sich sagen, dass für jeden Client, der auf eine Verbindung wartet, ein neuer Thread verwendet wird Wenn ein Client eine Verbindung anfordert, erstellt er einen neuen ConnectionHandler in einem neuen Thread (Hinweis: Verwenden Sie den von der Schnittstelle erstellten Thread-Körper. Dies bedeutet, dass im gesamten System möglicherweise ein Dutzend Threads ausgeführt werden. Der System-Overhead wird mit zunehmender Anzahl verbundener Clients weiter zunehmen. Wir können nicht umhin, die Möglichkeit in Betracht zu ziehen, dass das System ihn nicht ertragen kann, wenn der Overhead auf ein bestimmtes Niveau ansteigt Die Anzahl der verbundenen Clients erhöhen und die Effizienz des Servers verbessern.

Dann ist die Lösung: Auf der Serverseite erstellen wir beim Start des Servers eine bestimmte Anzahl von PooledConnectionHandlers. Wir legen die eingehenden Verbindungen in einem Verbindungspool ab Den Rest erledigt der PooledConnectionHandler. Das Client-Programm muss überhaupt nicht geändert werden. Die Vorteile dieses Designs sind wie folgt:

1.
2. Sie müssen den PooledConnectionHandler-Thread nur begrenzt oft starten

Die Bedeutung dieser beiden Sätze wird im folgenden Programm wiedergegeben. Das Folgende ist die Struktur der PooledRemoteFileServer-Klasse:

import java.io.*;
import java.net.*;
import java.util.*;

public class PooledRemoteFileServer {
PROtected int maxConnections;// Definieren Sie Clients, die gleichzeitig verarbeitet werden können. Die maximale Anzahl von Verbindungen protected int listenPort;//Definieren Sie die Portnummer, die überwacht werden soll
protected ServerSocket serverSocket;

public PooledRemoteFileServer(int aListenPort, int maxConnections) {
listenPort = aListenPort;
this .maxConnections = maxConnections;
}
public static void main(String[] args) {
}
public void setUpHandlers(){//Erstellen Sie einen PooledConnectionHandler mit einer Reihe von maxConnections
}
public void AcceptConnections(){//Überwachen Sie die eingehende Clientverbindung auf ServerSocket, was genau dem Abhörprogramm im vorherigen RemoteFileServer und MultiThreadRemoteFileServer entspricht
}
protected void handleConnection(Socket incomingConnection) {
}//Connection handler
}

In ähnlicher Weise schauen Sie sich zunächst die Hauptfunktion an
public static void main(String[] args) {
PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3) ;
server.setUpHandlers();//Im Gegensatz zur Hauptfunktion aller vorherigen Server müssen wir zunächst einen Verbindungspool erstellen, der über drei verfügbare ConnectionHandler verfügt
server.acceptConnections();//Sobald Sie bereit sind, beginnen Sie mit dem Zuhören

Sehen wir uns an, wie das Programm implementiert wird, um drei ConnectionHandler zu erstellen:

public void setUpHandlers(){
for (int i = 0; i < maxConnections ; i++) {
PooledConnectionHandler = new PooledConnectionHandler();
new Thread(currentHandler, "Handler " + i).start();
}
}

setUpHandlers( )-Methode erstellt maxConnections PooledConnectionHandlers und aktiviert sie in einem neuen Thread. PooledConnectionHandler ist ein Thread-Körper, der mit einer Schnittstelle implementiert ist (Runnable ermöglicht es uns, start() aufzurufen). auf dem Thread und erwarten, dass run() auf dem Runnable aufgerufen wird. Das heißt, unser PooledConnectionHandler wartet darauf, eingehende Verbindungen zu verarbeiten, jede in ihrem eigenen Thread. In unserem Beispiel erstellen wir nur drei Threads, und dies kann nicht geändert werden, sobald der Server läuft.

Die Methode „acceptConnections()“ wird hier weggelassen. Schauen Sie sich den Verbindungshandler handleConnection(Socket incomingConnection) an );
}

Hier ruft der Verbindungshandler direkt die Klassenmethode ProcessRequest der PooledConnectionHandler-Thread-Klasse auf, um die überwachte Verbindung zu verarbeiten.

PooledRemoteFileServer-Klasse Bei allen Methoden handelt es sich um eine wichtige Thread-Klasse, PooledConnectionHandler. Schauen wir uns an, wie eine solche mit einer Schnittstelle implementierte Klasse aussieht:

öffentliche Klasse PooledConnectionHandler implementiert Runnable {
geschützte Socket-Verbindung;// Stellt die dar Socket wird derzeit verarbeitet
protected static List pool = new LinkedList();//Der statische LinkedList-benannte Pool speichert die Verbindungen, die verarbeitet werden müssen, d. h. er verwendet LinkedList, um einen Verbindungspool zu simulieren
public PooledConnectionHandler() {//Konstruktor
}
public void handleConnection() {//Die E/A-Operation für die Verbindung ist hier
}
public static void processRequest(Socket requestToHandle) {//Verarbeiten Sie Kundenverbindungen und fügen Sie sie dem Verbindungspool hinzu
}
public void run() {//Warten Sie, bis eine Verbindung zustande kommt, rufen Sie handleConnection( auf. ) zur Verarbeitung
}
}

Es ist ersichtlich, dass diese Klasse dem ConnectionHandler des Multithread-Socket-Tages sehr ähnlich ist, der Unterschied besteht jedoch darin, dass sie über eine Möglichkeit verfügt, mit dem umzugehen Verbindungspool.

Schauen wir uns zunächst die Methode „processRequest()“ an, die im Listener-Programm verwendet werden soll
public static void ProcessRequest(Socket requestToHandle) {
synchronized (pool) {
pool.add(pool.size(), requestToHandle) ;
pool.notifyAll();
}
}

Das requestToHandle ist hier der zu verarbeitende Client-Verbindungs-Socket Man kann sagen, dass die Arbeit von ProcessRequest darin besteht, die Client-Verbindung zum Verbindungspool hinzuzufügen. Um jedoch sicherzustellen, dass es beim Betrieb des Verbindungspools (Pool) nicht zu Störungen durch andere Threads kommt, müssen Sie die Objektsperre erhalten Verbindungspool und die Verwendung von Synchronisierungsblöcken, die Sie zuvor gelernt haben, können hier verwendet werden.

Da wir nun sichergestellt haben, dass wir die einzigen sind, die „waten“, können wir Fügen Sie den eingehenden Socket am Ende der LinkedList hinzu. Sobald wir die neue Verbindung hinzugefügt haben, können Sie andere Threads, die auf den Pool warten, benachrichtigen Aus einer anderen Perspektive kann man auch sagen, dass einige Bedingungen bereits erfüllt sind.

Was ist dieser wartende Thread?

Lassen Sie uns die run()-Methode auf PooledConnectionHandler implementieren. Es wartet auf den Verbindungspool und verarbeitet es, sobald eine Verbindung im Pool besteht, sodass der Thread, der die Verbindung verarbeiten muss, wartet:

public void run() {
while (true) {
synchronized (pool) {
while (pool.isEmpty( )) {
try {
pool.wait();
} Catch (InterruptedException e) {return; }
}
connection = (Socket) pool.remove(0);/ /Ergreifen Sie die erste Verbindung im Pool und machen Sie daraus eine Verbindung, die bald verarbeitet wird
}
handleConnection(); //Übergeben Sie es dann zur Verarbeitung an handleConnection
}
}

Diese Funktion teilt uns mit, was jeder PooledConnectionHandler-Thread hauptsächlich ausführt. Offensichtlich wird ständig überprüft, ob verbundene Verbindungen vorhanden sind Wenn ja, wird es sofort verarbeitet, also wartet es auf eine Bedingung wie „verbunden“. Wenn ProcessRequest gerade eine Benachrichtigung sendet (pool.notify()), ist diese Bedingung erfüllt und der Handler in der Verbindung muss nicht mehr warten und kann sofort eine Verbindung zur Verarbeitung herstellen Bis diese Bedingung erfüllt ist, befindet sich der Thread, in dem sich wait() befindet, jedoch weiterhin in einem blockierten Zustand oder in einem stagnierenden Zustand. Da der Pool ebenfalls betrieben werden muss, müssen hier auch synchronisierte Blöcke verwendet werden 🎜>
Lassen Sie uns das Konzept der Objektsperren noch einmal überprüfen. Wenn run() die Mutex-Sperre des Pools besitzt, wie kann ProcessRequest() die Verbindung in den Pool einfügen? Die Antwort ist, dass ein Aufruf von „wait()“ für den Pool die Sperre aufhebt und „wait()“ dann die Sperre erneut greift, bevor es sich selbst zurückgibt. Dadurch kann anderer Synchronisierungscode für das Poolobjekt die Sperre erwerben.

Abschließend schauen wir uns die Methode handleConnection() im Thread PooledConnectionHandler an. Anders als bei einem Multithread-Server verfügt unser PooledConnectionHandler über eine Methode handleConnection(). Der Code dieser Methode ist genau derselbe wie der run()-Methode auf dem ConnectionHandler in einem nicht gepoolten Multithread-Server. Zuerst verpacken wir den OutputStream und den InputStream (mit getOutputStream() und getInputStream() auf dem Socket) in einen BufferedReader bzw. einen PrintWriter. Anschließend lesen wir die Zieldatei Zeile für Zeile ein, genau wie im Multithread-Beispiel. Wieder einmal holen wir uns einige Bytes, fügen sie in eine lokale Zeilenvariable ein und schreiben sie an den Client. Nach Abschluss der Lese- und Schreibvorgänge schließen wir den FileReader und öffnen den Stream.

Apropos, wir können sehen, dass es zwei Klassen im Programm gibt, PooledRemoteFileServer und PooledConnectionHandler, die Verbindungsanfragen nicht direkt verarbeiten, sondern nur für die Überwachung dieser Verbindungen und deren Rückgabe an den Verbindungspool verantwortlich sind. Die spezifischen Aspekte der Verarbeitung werden von PooledConnectionHandler verwaltet.

Unser Server mit Verbindungspool ist fertig. Sehen wir uns die Schritte zum Erstellen und Verwenden eines „gepoolten“ Servers an:

1. Erstellen Sie eine neue Art von Verbindungshandler (nennen wir ihn PooledConnectionHandler), um gepoolte Verbindungen zu verarbeiten.
2. Ändern Sie den Server, um einen Satz PooledConnectionHandler zu erstellen und zu verwenden.

Angehängt: Quellcode von PooledRemoteFileServer.java

import java.io.*;
import java.net.*;
import java.util.*;

public class PooledRemoteFileServer {
protected int maxConnections;
protected int listenPort;
protected ServerSocket serverSocket;
public PooledRemoteFileServer(int aListenPort, int maxConnections) {
listenPort = aListenPort;
this.maxConnections = maxConnections;
}
public void AcceptConnections() {
try {
ServerSocket server = new ServerSocket(listenPort, 5);
Socket incomingConnection = null;
while (true) {
incomingConnection = server.accept();
handleConnection(incomingConnection);
}
} Catch (BindException e) {
System .out.println("Unable to bind to port " + listenPort);
} Catch (IOException e) {
System.out.println("Unable to instanziate a ServerSocket on port: " + listenPort);
}
}
protected void handleConnection(Socket ConnectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}
public static void main(String[] args) {
PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3);
server.setUpHandlers();
server.acceptConnections();
}
public void setUpHandlers() {
for (int i = 0 ; i < maxConnections; i++) {
PooledConnectionHandler = new PooledConnectionHandler();
new Thread(currentHandler, "Handler " + i).start();
}
}
}

Klasse PooledConnectionHandler implementiert Runnable {
geschützte Socket-Verbindung;
geschützter statischer Listenpool = new LinkedList();
öffentlicher PooledConnectionHandler() {
}
öffentliche void handleConnection () {
try {
PrintWriter streamWriter = new PrintWriter(connection.getOutputStream());
BufferedReader streamReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

String fileToRead = streamReader.readLine();
BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));

String line = null;
while ((line = fileReader.readLine()) != null)
streamWriter.println(line);

fileReader.close();
streamWriter.close();
streamReader.close();
} Catch ( FileNotFoundException e) {
System.out.println("Die angeforderte Datei konnte auf dem Server nicht gefunden werden.");
} Catch (IOException e) {
System.out.println("Fehler bei der Verarbeitung eines Clients : " + e);
}
}
public static void ProcessRequest(Socket requestToHandle) {
synchronized (pool) {
pool.add(pool.size(), requestToHandle);
pool.notifyAll();
}
}
public void run() {
while (true) {
synchronized (pool) {
while (pool.isEmpty ()) {
try {
pool.wait();
} Catch (InterruptedException e) {
return;
}
}
connection = (Socket) pool .remove(0);
}
handleConnection();
}
}

相关内容请关注PHP中文网(www.php.cn)! 


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn