Heim  >  Artikel  >  Java  >  Java NIO: I/O-Modell

Java NIO: I/O-Modell

大家讲道理
大家讲道理Original
2017-05-28 11:32:331267Durchsuche

Vielleicht fällt es vielen Freunden etwas schwer, NIO zu lernen, und viele Konzepte darin sind nicht so klar. Bevor wir mit Java NIOProgrammierung beginnen, besprechen wir heute einige Grundkenntnisse: I/OModell. Der folgende Artikel beginnt mit den Konzepten von Synchronisation und Asynchronität, erklärt dann den Unterschied zwischen Blockierung und Nichtblockierung, stellt dann den Unterschied zwischen blockierender E/A und nicht blockierender E/A vor, stellt dann den Unterschied zwischen synchroner E/A und asynchroner E/A vor und führt dann ein 5 IO-Modelle und stellt schließlich zwei Entwurfsmuster (Reactor und Proactor) vor, die sich auf Hochleistungs-IO-Design beziehen.

Das Folgende ist die Inhaltsübersicht dieses Artikels:

1. Was ist Synchronisation? Was ist asynchron?

2. Was ist Blockieren? Was ist nicht blockierend?

3. Was blockiert IO? Was ist nicht blockierendes IO?

4. Was ist synchrones IO? Was ist asynchrones IO?

5. Fünf IO-Modelle

6. Zwei Hochleistungs-IO-Designmuster

Wenn etwas nicht stimmt, verzeihen Sie mir bitte und freuen Sie sich über Kritik und Korrekturen.

1. Was ist Synchronisation? Was ist asynchron?

Die Konzepte der Synchronisation und Asynchronität gibt es schon seit langem, und im Internet gibt es viele Meinungen zu Synchronisation und Asynchronität. Folgendes ist mein persönliches Verständnis:

Synchronisierung bedeutet: Wenn mehrere Aufgaben oder Ereignisse auftreten, müssen diese Aufgaben oder Ereignisse einzeln ausgeführt werden. Die Ausführung eines Ereignisses bzw Die Aufgabe führt dazu, dass der gesamte Prozess vorübergehend wartet und diese Ereignisse nicht gleichzeitig ausgeführt werden können.

Asynchron bedeutet: Wenn mehrere Aufgaben oder Ereignisse auftreten, können diese Ereignisse gleichzeitig ausgeführt werden, und die Ausführung eines Ereignisses oder einer Aufgabe wird nicht dazu führen, dass der gesamte Prozess fehlschlägt. Warten Sie.

Dies ist synchron und asynchron. Um ein einfaches Beispiel zu geben: Wenn es eine Aufgabe gibt, die zwei Unteraufgaben A und B enthält. Wenn A ausgeführt wird, kann B bei der Synchronisierung nur warten, bis A abgeschlossen ist, und dann kann B asynchron ausgeführt werden gleichzeitig ausgeführt werden, und B muss nicht darauf warten, dass A die Ausführung beendet, sodass die Ausführung von A nicht dazu führt, dass die gesamte Aufgabe vorübergehend wartet.

Wenn Sie es immer noch nicht verstehen, können Sie zuerst die folgenden zwei Codeteile lesen:




1

2

3

4

5

6

7

8

9

10

11

12

13

14


void fun1() {

      

  }

  

  void fun2() {

      

  }

  

  void function(){

      fun1();

      fun2()

      .....

      .....

  }


Dieser Code ist eine typische Synchronisierung. In der Methodenfunktion führt die Ausführung von fun2 dazu, dass Fun2 nicht ausgeführt werden kann, bevor die Ausführung von fun1 abgeschlossen wird es kann ausgeführt werden.

Dann schauen Sie sich den folgenden Code an:




1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


void fun1() {

     

}

 

void fun2() {

     

}

 

void function(){

    <a href="http://www.php.cn/wiki/165.html" target="_blank">new</a> Thread(){

        public void run() {

            fun1();

        }

    }.start();

     

    new Thread(){

        public void run() {

            fun2();

        }

    }.start();

 

    .....

    .....

}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

void fun1() {

}<p class="Zeilennummer4 index3 alt1"> </p> <p class="Zeilennummer5 index4 alt2"><code class="java keyword">void fun2() {

}

void function(){

 <a href="http://www.php.cn/wiki/165.html" target="_blank">new</a> Thread(){

                                                >public void run() {

code><code class="java plain">fun1();

}

}.start( );

</code-klasse></p>new Thread(){<p class="line number17 index16 alt2"><code class="Java Leerzeichen"> public void run() {

code><code class="java plain">fun2 ();

}

}.start( );

......<p class="Zeilennummer23 index22 alt2"><code class="Java Leerzeichen"> .....

}


Dieser Code ist ein typischer asynchroner Code. Die Ausführung von fun1 hat keinen Einfluss auf die Ausführung von fun2, und die Ausführung von fun1 und fun2 führt nicht dazu, dass der nachfolgende Ausführungsprozess vorübergehend ist. Warten.

Tatsächlich sind Synchronisation und Asynchronität sehr weit gefasste Konzepte. Ihr Fokus liegt darauf, ob das Auftreten oder die Ausführung eines Ereignisses dazu führt, dass der gesamte Prozess vorübergehend wartet, wenn mehrere Aufgaben und Ereignisse auftreten. Ich denke, wir können eine Analogie zwischen Synchronisation und Asynchronität mit dem Schlüsselwort synchronized in Java herstellen. Wenn mehrere Threads gleichzeitig auf eine Variable zugreifen, ist der Zugriff jedes Threads auf die Variable ein Ereignis. Zur Synchronisierung müssen diese Threads nacheinander auf die Variable zugreifen Threads müssen warten; bei asynchronen Threads müssen nicht mehrere Threads einzeln auf die Variable zugreifen, sondern können gleichzeitig darauf zugreifen.

Daher bin ich persönlich der Meinung, dass Synchronisation und Asynchronität auf viele Arten ausgedrückt werden können. Der Schlüssel zum Erinnern liegt jedoch darin, dass beim Auftreten mehrerer Aufgaben und Ereignisse unabhängig davon, ob das Auftreten oder die Ausführung eines Ereignisses den gesamten Prozess auslöst vorübergehend warten. Im Allgemeinen kann Asynchronität durch Multithreading erreicht werden. Denken Sie jedoch daran, Multithreading nicht mit Asynchronität gleichzusetzen. Asynchronität ist nur ein Makromuster, und die Verwendung von Multithreading zur Erzielung von Asynchronität ist nur ein Mittel, und eine asynchrone Implementierung kann ebenfalls erreicht werden durch Mehrfachverarbeitung.

2. Was ist Blockieren? Was ist nicht blockierend?

Der Unterschied zwischen Synchronisation und Asynchronität wurde bereits früher vorgestellt. In diesem Abschnitt werfen wir einen Blick auf den Unterschied zwischen Blockierung und Nichtblockierung.

Blockieren bedeutet: Wenn ein Ereignis oder eine Aufgabe ausgeführt wird, wird eine Anforderungsoperation ausgegeben. Da jedoch die für die Anforderungsoperation erforderlichen Bedingungen nicht erfüllt sind, wird dort gewartet, bis die Bedingungen erfüllt sind

Nicht blockierend bedeutet: Wenn ein Ereignis oder eine Aufgabe ausgeführt wird, wird eine Anforderungsoperation ausgegeben. Wenn die für die Anforderungsoperation erforderlichen Bedingungen nicht erfüllt sind, wird sofort eine Flag-Nachricht zurückgegeben, um darüber zu informieren, dass die Bedingungen nicht erfüllt sind , und es wird nicht fortgesetzt. Warten Sie dort.

Das ist der Unterschied zwischen Blockieren und Nichtblockieren. Das heißt, der Hauptunterschied zwischen Blockierung und Nichtblockierung besteht darin, dass, wenn eine Anforderung für eine Operation gestellt wird und die Bedingungen nicht erfüllt sind, ewig gewartet wird oder eine Flag-Nachricht zurückgegeben wird.

Geben Sie ein einfaches Beispiel:

Wenn ich den Inhalt einer Datei lesen möchte und zu diesem Zeitpunkt kein Inhalt zum Lesen in der Datei vorhanden ist, wird die Datei immer dort auf die Synchronisierung warten . Solange die Datei keinen lesbaren Inhalt enthält, wird eine Flag-Meldung direkt zurückgegeben, um zu informieren, dass die Datei keinen lesbaren Inhalt enthält.

Einige Freunde im Internet setzen Synchronisation und Asynchronität mit Blockieren bzw. Nichtblockieren gleich. Tatsächlich handelt es sich um zwei völlig unterschiedliche Konzepte. Beachten Sie, dass es für das Verständnis des nachfolgenden IO-Modells sehr wichtig ist, den Unterschied zwischen diesen beiden Konzeptsätzen zu verstehen.

Der Fokus von Synchronisation und Asynchronität liegt darauf, ob die Ausführung einer Aufgabe dazu führt, dass der gesamte Prozess vorübergehend wartet, während mehrere Aufgaben ausgeführt werden.

Der Fokus von Blockieren und Nichtblockieren liegt beim Ausgeben einer Anforderung Wenn während des Betriebs die Bedingungen für den Vorgang nicht erfüllt sind, wird eine Flag-Nachricht zurückgegeben, um darüber zu informieren, dass die Bedingungen nicht erfüllt sind.

Das Verständnis von Blockierung und Nichtblockierung kann analog zur Thread-Blockierung verstanden werden. Wenn ein Thread eine Anforderungsoperation ausführt und die Bedingung nicht erfüllt ist, wird er blockiert, dh er wartet darauf, dass die Bedingung erfüllt ist traf.

3. Was blockiert IO? Was ist nicht blockierendes IO?

Bevor wir das blockierende IO und das nicht blockierende IO verstehen, schauen wir uns zunächst an, wie der spezifische IO-Betriebsprozess ausgeführt wird.

Im Allgemeinen umfassen E/A-Vorgänge: Lesen und Schreiben auf die Festplatte, Lesen und Schreiben auf den Socket sowie Lesen und Schreiben auf die Peripheriegeräte.

Wenn ein Benutzer-Thread eine E/A-Anforderungsoperation initiiert (in diesem Artikel wird eine Leseanforderungsoperation als Beispiel verwendet), prüft der Kernel, ob die zu lesenden Daten bereit sind. Wenn die Daten nicht verfügbar sind, wird E/A blockiert bereit, es wird dort weiter warten, bis die Daten bereit sind; für nicht blockierende E/A wird eine Flag-Nachricht zurückgegeben, um den Benutzer-Thread darüber zu informieren, dass die aktuell zu lesenden Daten nicht bereit sind. Wenn die Daten bereit sind, werden sie in den Benutzerthread kopiert, sodass ein vollständiger E/A-Leseanforderungsvorgang abgeschlossen ist. Das heißt, ein vollständiger E/A-Leseanforderungsvorgang umfasst zwei Phasen:

 1) Ansicht Daten Ist es bereit?

 2) Daten kopieren (der Kernel kopiert die Daten in den Benutzerthread).

Dann besteht der Unterschied zwischen Blockierung (blockierende IO) und nicht blockierende (nicht blockierende IO) darin, dass Sie in der ersten Phase, wenn die Daten noch nicht bereit sind, weiter warten sollten, während Sie prüfen, ob die Daten bereit sind , oder direkt eine Flag-Nachricht zurückgeben.

Die traditionelle E/A in Java blockiert E/A, z. B. das Lesen von Daten über den Socket. Wenn die Daten nach dem Aufruf der read()-Methode nicht bereit sind, wird der aktuelle Thread beim Aufruf der Lesemethode bis dahin blockiert handelt es sich um Daten, und wenn es sich um nicht blockierende E/A handelt, sollte die read()-Methode eine Flag-Nachricht zurückgeben, um den aktuellen Thread darüber zu informieren, dass die Daten nicht bereit sind, anstatt ständig dort zu warten.

4. Was ist synchrones IO? Was ist asynchrones IO?

Schauen wir uns zunächst die Definitionen von synchronem IO und asynchronem IO im Buch „Unix Network Programming“ an:

  Ein synchroner E/A-Vorgang führt dazu, dass der anfordernde Prozess blockiert wird, bis dieser E/A-Vorgang abgeschlossen ist.
 Ein asynchroner E/A-Vorgang führt nicht dazu, dass der anfordernde Prozess blockiert wird.

Aus der wörtlichen Bedeutung geht hervor: synchrones IO bedeutet, dass der Thread blockiert wird, bevor der IO-Vorgang abgeschlossen ist, wenn a Der Thread fordert eine E/A-Operation an, der Thread wird blockiert, bevor die E/A-Operation abgeschlossen ist. E/A-Operationen führen nicht dazu, dass der Anforderungsthread blockiert wird.

Tatsächlich dienen die synchronen IO- und asynchronen IO-Modelle der Interaktion zwischen Benutzer-Threads und dem Kernel:

Für synchrone IO: nachdem der Benutzer eine IO-Anforderungsoperation ausgegeben hat, wenn die Daten vorhanden sind nicht bereit. Es muss kontinuierlich über den Benutzerthread oder den Kernel abgefragt werden. Wenn die Daten bereit sind, werden die Daten vom Kernel in den Benutzerthread kopiert Der E/A-Anforderungsvorgang wird vom Benutzerthread ausgegeben. Beide Phasen des E/A-Vorgangs werden vom Kernel automatisch abgeschlossen

. Anschließend wird eine Benachrichtigung gesendet, um den Benutzerthread darüber zu informieren, dass der E/A-Vorgang abgeschlossen wurde. Das heißt, bei asynchroner E/A werden Benutzer-Threads nicht blockiert.

Dies ist der Hauptunterschied zwischen synchroner E/A und asynchroner E/A. Der Hauptunterschied zwischen synchroner E/A und asynchroner E/A spiegelt sich darin wider, ob die Datenkopierphase vom Benutzerthread oder vom Kernel abgeschlossen wird. Daher muss asynchrones E/A eine zugrunde liegende Unterstützung durch das Betriebssystem haben. Beachten Sie, dass synchrone E/A und asynchrone E/A zwei verschiedene Konzepte sind als blockierende E/A und nicht blockierende E/A.

Blockierende E/A und nicht blockierende E/A spiegeln sich darin wider, dass der Benutzerthread immer noch eine Flag-Nachricht erhält, wenn der Benutzer eine E/A-Operation anfordert und die Daten nicht bereit sind, wenn er auf die Daten wartet bereit sein. Mit anderen Worten, blockierende E/A und nicht blockierende E/A spiegeln sich in der ersten Phase der E/A-Operation wider, nämlich in der Art und Weise, wie sie verarbeitet wird, wenn überprüft wird, ob die Daten bereit sind.

5. Fünf IO-Modelle

Im Buch „Unix Network Programming“ werden fünf IO-Modelle erwähnt, nämlich: Blocking IO, Non-Blocking IO, Multiplexed IO, Signal

Drives

IO und asynchrones IO.

Lassen Sie uns die Gemeinsamkeiten bzw. Unterschiede dieser 5 IO-Modelle vorstellen. 1. Blockierendes IO-Modell

Das traditionellste IO-Modell, das heißt, die Blockierung erfolgt während des Lese- und Schreibvorgangs von Daten.

Wenn der Benutzerthread eine E/A-Anfrage ausgibt, prüft der Kernel, ob die Daten bereit sind. Wenn nicht, wartet er, bis die Daten bereit sind, und der Benutzerthread befindet sich im blockierten Zustand

Zustand

, und der Benutzerthread übergibt die CPU. Wenn die Daten bereit sind, kopiert der Kernel die Daten in den Benutzerthread und gibt das Ergebnis an den Benutzerthread zurück. Anschließend gibt der Benutzerthread den Blockierungsstatus frei.

Ein Beispiel für ein typisches blockierendes IO-Modell ist:



1


data = socket.read();


Wenn die Daten nicht bereit sind, werden sie in der Lesemethode immer blockiert.

2. Nicht blockierendes IO-Modell

Wenn der Benutzer-Thread einen Lesevorgang initiiert, muss nicht gewartet werden, sondern es wird sofort ein Ergebnis erhalten. Wenn das Ergebnis ein Fehler ist, weiß es, dass die Daten noch nicht bereit sind, und kann den Lesevorgang erneut senden. Sobald die Daten im Kernel bereit sind und erneut eine Anfrage vom Benutzerthread empfangen wird, kopiert dieser die Daten sofort in den Benutzerthread und kehrt dann zurück.

Tatsächlich muss der Benutzerthread im nicht blockierenden IO-Modell den Kernel ständig fragen, ob die Daten bereit sind, was bedeutet, dass nicht blockierende IO die CPU nicht übergibt, sondern immer belegen die CPU.

Das typische nicht blockierende IO-Modell sieht im Allgemeinen wie folgt aus:




1

1

2

3

4

5

6

7


<a href="http://www.php.cn/wiki/121.html" target="_blank">while</a>(true){

    data = socket.read();

    <a href="http://www.php.cn/wiki/109.html" target="_blank">if</a>(data!= error){

        处理数据

        <a href="http://www.php.cn/wiki/130.html" target="_blank">break</a>;

    }

}

2

3

4

5

6

7

<a href="http://www.php.cn/wiki/121.html" target=" _blank"> while</a>(true) { code><p class="Zeilennummer2 index1 alt1"><code class="Java Leerzeichen"> data = socket.read(); <p class="Zeilennummer3 index2 alt2"><code class="Java-Leerzeichen"> <a href="http://www.%20php.cn%20/wiki/109.html" target="_blank">if</a>(data!= error){

Verarbeitung von Daten

<a href="http://www.php.cn/wiki/130.html" target="_blank"> break a></a>;

}

}


Es gibt jedoch ein sehr ernstes Problem mit nicht blockierendem IO. In der while-Schleife müssen Sie ständig fragen, ob die Kerneldaten bereit sind, was der Fall ist Ursache CPU-Auslastung Die Rate ist sehr hoch, daher werden Schleifen im Allgemeinen selten zum Lesen von Daten verwendet.

3. Multiplex-IO-Modell

Das Multiplex-IO-Modell ist derzeit das am häufigsten verwendete Modell. Java NIO ist eigentlich Multiplex-IO.

Im Multiplex-IO-Modell gibt es einen Thread, der kontinuierlich den Status mehrerer Sockets abfragt. Nur wenn der Socket tatsächlich Lese- und Schreibereignisse hat, werden die eigentlichen IO-Lese- und Schreibvorgänge tatsächlich aufgerufen. Da im Multiplex-E/A-Modell nur ein Thread zum Verwalten mehrerer Sockets verwendet werden kann, muss das System weder neue Prozesse oder Threads erstellen noch diese Threads und Prozesse verwalten, und zwar nur, wenn tatsächlich Sockets gelesen und gelesen werden Beim Schreiben von Ereignissen werden E/A-Ressourcen nur dann verwendet, wenn die Zeit abgelaufen ist, sodass die Ressourcennutzung erheblich reduziert wird.

In Java NIO wird selector.select() verwendet, um abzufragen , ob für jeden Kanal ein Ankunftsereignis vorliegt. Wenn es kein Ereignis gibt, wird es dort immer blockiert, also dies Die Methode führt zur Blockierung des Benutzerthreads.

Vielleicht werden einige Freunde sagen, dass ich Multithreading + blockierende E / A verwenden kann, um ähnliche Effekte zu erzielen. Bei Multithreading + blockierenden E / A entspricht jedoch jeder Socket einem Thread, was eine Menge Ressourcen verursacht Verwendung und insbesondere bei langen Verbindungen werden Thread-Ressourcen nie freigegeben. Wenn später viele Verbindungen vorhanden sind, führt dies zu einem Leistungsengpass.

Im Multiplex-IO-Modus können mehrere Sockets über einen Thread verwaltet werden. Nur wenn der Socket tatsächlich Lese- und Schreibereignisse hat, werden Ressourcen für tatsächliche Lese- und Schreibvorgänge belegt. Daher eignet sich Multiplex-E/A besser für Situationen, in denen die Anzahl der Verbindungen groß ist.

Darüber hinaus ist Multiplexed IO effizienter als das nicht blockierende IO-Modell, weil bei nicht blockierendem IO der Socket-Status ständig über den Benutzerthread abgefragt wird, während bei Multiplexed IO der Socket-Status abgefragt wird Der Status jedes Sockets wird vom Kernel durchgeführt, und diese Effizienz ist viel höher als die von Benutzer-Threads.

Es ist jedoch zu beachten, dass das Multiplex-IO-Modell mithilfe von Abfragen erkennt, ob ein Ereignis eingetroffen ist, und nacheinander auf die eintreffenden Ereignisse reagiert. Daher werden beim Multiplex-E/A-Modell nachfolgende Ereignisse lange Zeit nicht verarbeitet, sobald der Ereignisantworttext groß ist, und die Abfrage neuer Ereignisse wird beeinträchtigt.

4. Signalgesteuertes E/A-Modell

Wenn der Benutzerthread im signalgesteuerten E/A-Modell eine E/A-Anforderungsoperation initiiert, wird eine Signal--Funktion registriert Der entsprechende Socket wird dann weiter ausgeführt. Wenn die Kerneldaten bereit sind, wird ein Signal an den Benutzerthread gesendet, der die E/A-Lese- und Schreibvorgänge im Signal aufruft Funktion zum Ausführen der eigentlichen E/A-Anforderungsoperation.

5. Asynchrones IO-Modell

Das asynchrone IO-Modell ist das idealste IO-Modell. Wenn der Benutzer-Thread den Lesevorgang initiiert, kann er sofort gestartet werden Andere Dinge. Aus der Sicht des Kernels wird andererseits beim Empfang eines asynchronen Lesevorgangs sofort zurückgegeben, was darauf hinweist, dass die Leseanforderung erfolgreich initiiert wurde, sodass kein Block für den Benutzerthread generiert wird. Anschließend wartet der Kernel auf den Abschluss der Datenvorbereitung und kopiert die Daten dann in den Benutzerthread. Wenn dies alles abgeschlossen ist, sendet der Kernel ein Signal an den Benutzerthread, um ihm mitzuteilen, dass der Lesevorgang abgeschlossen ist. Mit anderen Worten: Der Benutzerthread muss nicht wissen, wie die gesamte E/A-Operation tatsächlich ausgeführt wird. Er muss lediglich zuerst eine Anfrage initiieren. Wenn er das vom Kernel zurückgegebene Erfolgssignal empfängt, bedeutet dies, dass die E/A-Operation abgeschlossen ist und die Daten können direkt genutzt werden.

Mit anderen Worten, im asynchronen IO-Modell blockiert keine Phase des IO-Vorgangs den Benutzer-Thread. Beide Phasen werden vom Kernel automatisch abgeschlossen und dann wird ein Signal gesendet, um den Benutzer-Thread darüber zu informieren Der Vorgang wurde abgeschlossen. Für bestimmte Lese- und Schreibvorgänge ist es nicht erforderlich, die IO-Funktion im Benutzerthread erneut aufzurufen. Dies unterscheidet sich vom signalgesteuerten Modell. Wenn der Benutzerthread beim signalgesteuerten Modell das Signal empfängt, bedeutet dies, dass die Daten bereit sind. Anschließend muss der Benutzerthread die E/A-Funktion aufrufen, um den eigentlichen Lesevorgang durchzuführen Schreibvorgänge; im asynchronen E/A-Modell zeigt der Empfang des Signals an, dass der E/A-Vorgang abgeschlossen wurde und es keine Notwendigkeit gibt, die iO-Funktion im Benutzerthread für tatsächliche Lese- und Schreibvorgänge aufzurufen.

Beachten Sie, dass asynchrone E/A eine zugrunde liegende Unterstützung durch das Betriebssystem erfordert. In Java 7 wird asynchrone E/A bereitgestellt.

Die ersten vier E/A-Modelle sind tatsächlich synchrone E/A, und nur das letzte ist wirklich asynchrone E/A, denn unabhängig davon, ob es sich um Multiplex-E/A oder ein signalgesteuertes Modell handelt, wird die zweite Stufe des E/A-Betriebs verursacht Der Benutzer-Thread ist blockiert, dh der Prozess des Datenkopierens durch den Kernel führt dazu, dass der Benutzerthread blockiert wird.

6. Zwei Hochleistungs-IO-Designmuster

Unter den traditionellen Netzwerkdienst-Designmustern gibt es zwei klassische Muster:

Eines ist Multithreading, eines ist das Thread-Pool.

Für den Multithread-Modus, das heißt, wenn der Client kommt, erstellt der Server einen neuen Thread, um die Lese- und Schreibereignisse des Clients zu verarbeiten, wie in der folgenden Abbildung dargestellt:

Obwohl dieser Modus einfach und bequem zu handhaben ist, beansprucht er viele Ressourcen, da der Server einen Thread zum Verarbeiten jeder Clientverbindung verwendet. Wenn daher die Anzahl der Verbindungen die Obergrenze erreicht und ein anderer Benutzer eine Verbindung anfordert, führt dies direkt zu einem Ressourcenengpass und in schweren Fällen direkt zum Absturz des Servers.

Um die durch das Ein-Thread-Ein-Client-Modell verursachten Probleme zu lösen, wurde eine Thread-Pool-Methode vorgeschlagen, bei der ein Thread-Pool mit fester Größe und ein Client-Thread erstellt werden Der Pool benötigt einen inaktiven Thread zur Verarbeitung. Wenn der Client die Lese- und Schreibvorgänge abschließt, übergibt er die Belegung des Threads. Dadurch wird die durch die Erstellung von Threads für jeden Client verursachte Ressourcenverschwendung vermieden, sodass Threads wiederverwendet werden können.

Der Thread-Pool hat jedoch auch Nachteile. Wenn es sich bei den meisten Verbindungen um lange Verbindungen handelt, kann es sein, dass alle Threads im Thread-Pool für einen bestimmten Zeitraum belegt sind. Wenn dann ein anderer Benutzer eine Verbindung anfordert , weil es keine gibt Wenn die verfügbaren Leerlauf-Threads für die Verarbeitung verwendet werden, schlägt die Clientverbindung fehl, was sich auf die Benutzererfahrung auswirkt. Daher eignet sich der Thread-Pool besser für eine große Anzahl von Kurzverbindungsanwendungen.

Daher sind die folgenden zwei Hochleistungs-IO-Entwurfsmuster entstanden: Reactor und Proactor.

Im Reaktormodus werden die relevanten Ereignisse zuerst für jeden Client registriert, und dann fragt ein Thread jeden Client ab, um zu sehen, ob ein Ereignis auftritt. Die Ereignisse werden von jedem Client nacheinander verarbeitet. Wenn alle Ereignisse verarbeitet sind , werden sie zur Fortsetzung der Abfrage übertragen, wie in der folgenden Abbildung dargestellt:

Wie hier zu sehen ist, ist die oben Das Multiplex-IO unter den fünf IO-Modellen übernimmt den Reaktormodus. Beachten Sie, dass die obige Abbildung zeigt, dass jedes Ereignis nacheinander verarbeitet wird, um die Geschwindigkeit der Ereignisverarbeitung zu verbessern. Natürlich können Ereignisse über Multithreads oder Thread-Pools verarbeitet werden.

Wenn im Proactor-Modus ein Ereignis erkannt wird, wird ein neuer asynchroner Vorgang gestartet und dann zur Verarbeitung an den Kernel-Thread übergeben. Wenn der Kernel-Thread den E/A-Vorgang abschließt, wird eine Benachrichtigung gesendet Es ist bekannt, dass das asynchrone E/A-Modell den Proactor-Modus verwendet.

Das obige ist der detaillierte Inhalt vonJava NIO: I/O-Modell. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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