Technologieaustausch

Vollständige Kenntnisse im Java-Netzwerkmodell

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Überblick

Beschreibt die Grundkenntnisse von NIO auf Ava-Ebene zur grundlegenden Überprüfung

1. NIO-Übersicht

In Java ist NIO (Non-blocking I/O oder New I/O) ein neuer Satz von Eingabe-/Ausgabeoperations-APIs, die in Java SE 1.4 und nachfolgenden Versionen eingeführt wurden.

​ Im Vergleich zum herkömmlichen E/A-Modell bietet es eine höhere Effizienz und bessere Möglichkeiten zur gleichzeitigen Verarbeitung. Das Hauptmerkmal von NIO ist seine nicht blockierende Funktion, die es einem einzelnen Thread ermöglicht, mehrere E/A-Kanäle zu verwalten und dadurch die Anwendungsleistung in Szenarien mit hoher Parallelität erheblich zu verbessern.

Das Einsatzszenario von Java NIO eignet sich besonders für Situationen, in denen eine große Anzahl gleichzeitiger Verbindungen verarbeitet werden muss. In einem Netzwerkserver kann beispielsweise ein Thread Tausende von Client-Verbindungen verwalten, ohne dass jeder Verbindung ein unabhängiger Thread zugewiesen werden muss . Kann den Ressourcenverbrauch des Systems erheblich reduzieren und die Verarbeitungsfähigkeiten verbessern

2. Drei Hauptkomponenten von NIO in Java

1. Kanäle

Kanal ist das Medium des Datenflusses in Java NIO. Er ist bidirektional und kann zum Lesen oder Schreiben von Daten verwendet werden. Der Hauptvorteil von Kanälen besteht darin, dass sie nicht blockierend sind, was bedeutet, dass ein Thread mehrere Kanäle verwalten kann. Wenn auf einem Kanal keine Ereignisse auftreten, wird der Thread nicht blockiert und kann stattdessen andere Aufgaben verarbeiten. Zu den wichtigsten Kanaltypen gehören:

  • FileChannel: Wird zum Lesen und Schreiben von Dateien verwendet. Es kann zum Schreiben von Daten aus dem Puffer in die Datei oder zum Lesen von Daten aus der Datei in den Puffer verwendet werden.
  • SocketChannel: Wird für TCP-Verbindungen in der Netzwerkkommunikation verwendet und kann zum Lesen und Schreiben von Daten verwendet werden.
  • ServerSocketChannel: Wird zum Akzeptieren neuer SocketChannel-Verbindungen verwendet, ähnlich wie beim herkömmlichen ServerSocket.
  • DatagramChannel: Wird für die UDP-Kommunikation verwendet, die Datagramme senden und empfangen kann.

2. Puffer

​Puffer ist ein Objekt zum Speichern von Daten in NIO. Es handelt sich um ein Byte-Array, das Daten schreiben und daraus lesen kann. Ein Puffer hat eine bestimmte Kapazität und zwei wichtige Eigenschaften: Position und Grenze.

  • Kapazität: Die maximale Anzahl von Elementen, die der Puffer speichern kann.
  • Position: Der Index des aktuell bearbeiteten Elements, der sich ändert, wenn Daten gelesen oder geschrieben werden.
  • Grenze: Im Lesemodus der maximale Wert, den die Position erreichen kann; im Schreibmodus kann die Position den Wert nicht überschreiten.

Die wichtigsten Arten von Puffern sindByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferUndDoubleBuffer, jeder Typ entspricht einem primitiven Datentyp.

3. Selektoren

Ein Selektor ist ein Multiplexer in NIO, der es einem einzelnen Thread ermöglicht, Ereignisse von mehreren Kanälen zu überwachen, z. B. Lese-, Schreib-, Verbindungs- und Empfangsereignisse. Der Selektor benachrichtigt die Anwendung, wenn einer der Kanäle für E/A-Vorgänge bereit ist. Die Verwendung von Selektoren verbessert die Parallelitätsverarbeitungsfähigkeiten von Netzwerkanwendungen erheblich, da nicht für jede Verbindung ein Thread erstellt werden muss.

Zu den wichtigsten Auswahlmethoden gehören:

  • select(): Blockiert, bis mindestens ein Kanal für E/A-Vorgänge bereit ist.
  • selectedKeys(): Gibt ein Set zurück, das die SelectionKey-Objekte aller vorbereiteten Kanäle enthält.
  • wakeup(): Interrupt blockiertselect()überweisen.

SelectionKeyEs handelt sich um die Zuordnung zwischen Selektoren und Kanälen. Es stellt den Registrierungsstatus eines Kanals auf dem Selektor dar, einschließlich Kanälen, Selektoren, interessierten Ereignissammlungen und bereiten Ereignissammlungen.

3. Netzwerkprogrammierung

1. I/O blockieren

Dies ist das traditionellste E/A-Modell. In Java basieren die traditionellen Socket- und ServerSocket-APIs auf dem Blockieren von E/A. Wenn in diesem Modus ein Thread einen Lese- oder Schreibvorgang initiiert, wird der Thread blockiert, bis der E/A-Vorgang abgeschlossen ist. Wenn für den Lesevorgang keine Daten zum Lesen vorhanden sind oder der Schreibvorgang nicht sofort abgeschlossen werden kann, wartet der Thread, bis der Vorgang abgeschlossen ist.

Merkmale:

  • Einfaches Programmiermodell: Leicht zu verstehen und umzusetzen.
  • Ressourcenbelegung: Jede Verbindung erfordert einen unabhängigen Thread, was zu einer begrenzten Anzahl von Threads und einem hohen Ressourcenverbrauch führt.
  • Leistungseinschränkungen: In Szenarien mit hoher Parallelität sind Thread- und Kontextwechsel teuer und können leicht zu Engpässen führen.

2. Nicht blockierende E/A

​ Nicht blockierendes I/O ist Teil des Java NIO (New I/O)-Frameworks, das es Threads ermöglicht, Lese- und Schreibvorgänge zu initiieren, ohne blockiert zu werden. Wenn für einen Lesevorgang keine Daten zum Lesen vorhanden sind oder der Schreibvorgang nicht sofort abgeschlossen werden kann, wird der Thread nicht angehalten und kann weiterhin andere Aufgaben ausführen.

Merkmale:

  • Flexibilität: Threads können mehr Verbindungen verarbeiten, da sie nicht beim Warten auf E/A-Vorgänge blockiert werden.
  • Erhöhte Komplexität: Der Programmierer muss explizit prüfen, ob der Vorgang abgeschlossen ist, was die Programmierung erschwert.
  • Leistungsverbesserungen: In Szenarien mit hoher Parallelität kann der durch Threadwechsel verursachte Overhead erheblich reduziert werden.

3. Multiplexen

Beim Multiplexen werden mehrere Dateideskriptoren gleichzeitig über einen Thread überwacht und ein Deskriptor nur dann bearbeitet, wenn er bereit ist (normalerweise bedeutet dies, dass die Daten lesbar sind oder der Schreibpuffer beschreibbar ist). Selektoren werden in Java verwendet, um Multiplexing zu implementieren.

Merkmale:

  • Hohe Parallelität: Ein Thread kann Tausende von Verbindungen verarbeiten, wodurch der Durchsatz des Servers erheblich verbessert wird.
  • Effizient: Nur wenn eine E/A-Operation bereit ist, wird der Thread aktiviert, um unnötigen Thread-Wechsel zu vermeiden.
  • Ressourcenschonend: Im Vergleich zum Blockieren von E/A können Server im Multiplex-Modell Systemressourcen effizienter nutzen.

Beachten:

In praktischen Anwendungen werden nicht blockierende E/A häufig in Verbindung mit Multiplexing verwendet. Beispielsweise kann ein Server einen Selector verwenden, um mehrere SocketChannels zu überwachen. Wenn ein SocketChannel über Daten verfügt, die gelesen oder geschrieben werden können, benachrichtigt der Selector den Server und der Server verarbeitet diesen spezifischen SocketChannel auf nicht blockierende Weise.

4.NIO VS. BIO

4.1 Stream vs. Kanal

In Java sind Stream und Channel zwei verschiedene Methoden zur Verarbeitung von Datenströmen. Sie gehören zur Standard-IO-Bibliothek bzw. zur NIO-Bibliothek. Hier ein detaillierter Vergleich der beiden Konzepte:

Strom

Stream ist Teil des Standard-IO-Modells (Blocking IO) von Java, das eine Möglichkeit bietet, Daten sequenziell zu lesen und zu schreiben. Stream ist in zwei Typen unterteilt: InputStream und OutputStream, die zum Lesen bzw. Schreiben von Daten verwendet werden.Diese Streams können Byte-Streams sein (z. BInputStream, OutputStream) oder ein Zeichenstrom (z. BReader, Writer)。

Merkmale:

  • Obstruktiv: Standardmäßig blockieren Stream-Vorgänge. Das heißt, wenn im Lesevorgang keine Daten zum Lesen vorhanden sind oder der Schreibvorgang nicht sofort abgeschlossen werden kann, wird der aufrufende Thread blockiert, bis der Vorgang abgeschlossen ist.
  • Unidirektionalität: Jeder Stream hat eine Richtung, entweder schreibgeschützt oder schreibgeschützt.
  • automatische Abschaltung: Nachdem Sie Stream verwendet haben, müssen Sie es normalerweise aufrufenclose() Methode zur Freigabe von Ressourcen.Ab Java 7 kann die try-with-resources-Anweisung die Implementierung automatisch schließenAutoCloseableSchnittstellenressourcen, einschließlich Stream.

Kanal

Channel ist Teil des Java NIO-Modells (New IO), das eine höhere Abstraktionsebene als Stream bietet und so eine effizientere Datenverarbeitung ermöglicht. Kanäle können bidirektional sein, d. h. sie können zum Lesen und Schreiben von Daten verwendet werden.Zu den wichtigsten Kanaltypen gehören:FileChannelSocketChannelServerSocketChannelUndDatagramChannel

Merkmale:

  • nicht blockierend :Kanal kann im blockierenden oder nicht blockierenden Modus konfiguriert werden. Wenn im nicht blockierenden Modus für einen Lesevorgang keine Daten zum Lesen vorhanden sind oder ein Schreibvorgang nicht sofort abgeschlossen werden kann, kehrt der Vorgang sofort zurück, anstatt den Thread zu blockieren.
  • Pufferoperationen:Kanaloperationen werden immer über den Puffer (Puffer) ausgeführt. Daten werden vom Kanal in den Puffer gelesen oder vom Puffer in den Kanal geschrieben.
  • Multiplexen: Channel kann zusammen mit Selector verwendet werden, um Multiplexing zu erreichen, sodass ein Thread die E/A-Vorgänge mehrerer Kanäle überwachen und die Fähigkeiten zur gleichzeitigen Verarbeitung verbessern kann.

Zusammenfassen

  • Anwendbare Szene: Stream eignet sich besser für die Verarbeitung kleinerer Datensätze oder einfacher Dateivorgänge, während Channel besser für die Verarbeitung großer Datenmengen, insbesondere Netzwerkkommunikation und große Dateivorgänge, geeignet ist, da es eine höhere Leistung und bessere Parallelitätsfunktionen bietet.
  • Komplexität der Programmierung: Die API von Stream ist relativ einfach und leicht zu verwenden, während die API von Channel und NIO komplexer ist, aber leistungsfähigere Funktionen und eine höhere Effizienz bietet.
  • Resourcenmanagement: Unabhängig davon, ob es sich um einen Stream oder einen Kanal handelt, sollten Ressourcen angemessen verwaltet werden und sichergestellt werden, dass sie geschlossen werden, wenn sie nicht mehr benötigt werden, um Ressourcenlecks zu vermeiden.

4.2. IO-Modell

In Java können E/A-Operationsmodelle anhand der beiden Dimensionen synchron/asynchron und blockierend/nicht blockierend klassifiziert werden.

4.2.1 Synchrone blockierende E/A

Definition : Synchrones Blockieren von E/A ist das traditionellste E/A-Modell. Wenn ein Thread eine E/A-Operation aufruft (z. B. Lesen oder Schreiben), wird der Thread blockiert, bis die Operation abgeschlossen ist. Dies bedeutet, dass der Thread keine anderen Aufgaben ausführen kann, bis der Vorgang abgeschlossen ist.

Merkmale

  • Einfach und benutzerfreundlich, die Codelogik ist klar.
  • Jeder E/A-Vorgang erfordert einen exklusiven Thread, was für Szenarien mit hoher Parallelität nicht geeignet ist und zur Erschöpfung der Thread-Ressourcen führen kann.
  • Wird häufig verwendet inInputStreamOutputStreamSocketUndServerSocketSzene.

Beispiel:

Verwenden Sie traditionellInputStreamUndOutputStreamSo lesen und schreiben Sie Dateien:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class SyncBlockingIOExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt")) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.2.2 Synchrone nicht blockierende E/A

Definition : Bei synchroner nicht blockierender E/A wird der Thread nach dem Aufruf der E/A-Operation nicht blockiert. Wenn der Vorgang nicht sofort abgeschlossen werden kann, kehrt die Methode sofort zurück und gibt normalerweise einen Sonderwert (z. B. -1 oder 0) zurück oder löst eine Ausnahme aus, um anzuzeigen, dass der Vorgang nicht abgeschlossen ist.

Merkmale

  • Der Vorgangsstatus muss abgefragt werden, bis der Vorgang abgeschlossen ist.
  • Höhere Thread-Auslastung, da Threads andere Aufgaben ausführen können, während sie auf E/A warten.
  • Die Implementierung ist komplexer und erfordert Abfragen und Statusprüfungen.
  • Basierend auf Java NIOChannelsUndBuffers, durch AnrufconfigureBlocking(false)Stellen Sie den Kanal auf den nicht blockierenden Modus ein.

Beispiel:

Mit NIOFileChannelFür blockierungsfreies Lesen und Schreiben:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;

public class SyncNonBlockingIOExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("input.txt");
             FileOutputStream fos = new FileOutputStream("output.txt");
             FileChannel inChannel = fis.getChannel();
             FileChannel outChannel = fos.getChannel()) {
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            inChannel.configureBlocking(false);
            while (inChannel.read(buffer) > 0) {
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

4.2.3 Synchrone Multiplex-E/A

Definition: Verwendung des synchronen Multiplex-E/A-ModellsSelector(Auswahl), um mehrere zu überwachenChannel Ereignis.Wenn der Thread anruftSelector.select(), wird es blockiert, bis mindestens einer vorhanden istChannelEs treten Ereignisse (z. B. lesbar, beschreibbar, Verbindungsanfrage usw.) auf.

Merkmale

  • Ermöglicht einem Thread die Verwaltung mehrerer ThreadsChannel, verbessert die Parallelität.
  • passierenSelectorUndSelectionKeyMechanismus, der eine große Anzahl gleichzeitiger Verbindungen effizient verarbeiten kann.
  • Geeignet für Netzwerkserverszenarien wie Webserver und Chatserver.
  • Basierend auf dem Java NIO-Framework.

Beispiel:

Mit NIOFileChannelFür blockierungsfreies Lesen und Schreiben:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

public class SyncMultiplexingIOExample {
    public static void main(String[] args) throws IOException {
        try (Selector selector = Selector.open();
             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                if (selector.select() > 0) {
                    Set<SelectionKey> keys = selector.selectedKeys();
                    for (SelectionKey key : keys) {
                        if (key.isAcceptable()) {
                            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                            SocketChannel clientChannel = ssc.accept();
                            clientChannel.configureBlocking(false);
                            clientChannel.register(selector, SelectionKey.OP_READ);
                        }
                    }
                    keys.clear();
                }
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

4.2.4 Asynchrone nicht blockierende E/A

Definition : Im asynchronen nicht blockierenden E/A-Modell kehrt der Thread sofort nach dem Initiieren des E/A-Vorgangs zurück, ohne auf den Abschluss des Vorgangs zu warten. Wenn der Vorgang abgeschlossen ist, wird der Thread asynchron über eine Rückruffunktion oder eine Ereignisbenachrichtigung benachrichtigt.

Merkmale

  • Beim effizientesten E/A-Modell werden Threads überhaupt nicht blockiert und können sofort andere Aufgaben ausführen.
  • passierenAsynchronousChannelSchnittstelle und ihre Unterklassenimplementierung, wie zAsynchronousFileChannelUndAsynchronousSocketChannel
  • Geeignet für Szenarien mit hoher Parallelität und hoher Leistung, z. B. Big-Data-Verarbeitung und Netzwerkserver mit hoher Auslastung.
  • Mit Java 7 wurde das asynchrone, nicht blockierende I/O-Modell eingeführt.

Beispiel:

Mit NIOAsynchronousFileChannelFühren Sie asynchrones Lesen und Schreiben von Dateien durch:

import java.io.IOException;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.ByteBuffer;
import java.util.concurrent.CountDownLatch;

public class AsyncNonBlockingIOExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        AsynchronousFileChannel inChannel = AsynchronousFileChannel.open(Paths.get("input.txt"), StandardOpenOption.READ);
        AsynchronousFileChannel outChannel = AsynchronousFileChannel.open(Paths.get("output.txt"), StandardOpenOption.WRITE);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        CountDownLatch latch = new CountDownLatch(1);
        inChannel.read(buffer, 0, buffer, 0, (channel, result) -> {
            buffer.flip();
            outChannel.write(buffer, 0, buffer, 0, null);
            latch.countDown();
        });
        latch.await();
        inChannel.close();
        outChannel.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Hinweis: Das asynchrone Modell erfordert Unterstützung durch das zugrunde liegende Betriebssystem (Kernel).

  • Windows-Systeme implementieren echte asynchrone E/A über IOCP
  • Asynchrone E/A im Linux-System wurde in Version 2.6 eingeführt, die zugrunde liegende Implementierung verwendet jedoch weiterhin Multiplexing, um asynchrone E/A zu simulieren, und es gibt keinen Leistungsvorteil.

4.2.5 Asynchrone blockierende E/A

Definition : Theoretisch existiert dieses Modell nicht, da „asynchron“ bedeutet, dass der Vorgang im Hintergrund ausgeführt wird und der Thread nicht blockiert ist. Daher ist asynchrones Blockieren von E/A ein widersprüchliches Konzept und wird in der Praxis nicht vorkommen.

4.2.6 Zusammenfassung

Die Wahl des geeigneten I/O-Modells ist für die Leistung und das Ressourcenmanagement von entscheidender Bedeutung. In Szenarien mit hoher Parallelität sind synchrone, nicht blockierende E/A- oder synchrone, gemultiplexte E/A-Modelle die häufigste Wahl. In Szenarien, die extreme Leistung und Reaktionsgeschwindigkeit erfordern, ist das asynchrone, nicht blockierende E/A-Modell die erste Wahl.

4.3 Nullkopie

Zero-Copy-Konzept

​Zero-Copy-Technologie bedeutet, dass während des Prozesses der Datenübertragung von einem Ort zum anderen keine Daten zwischen User-Space und Kernel-Space kopiert werden müssen oder zumindest die Anzahl solcher Kopien reduziert wird, wodurch die Systemeffizienz verbessert wird. Bei herkömmlichen E/A-Vorgängen werden Daten beim Lesen aus dem Netzwerk oder der Festplatte zunächst in den Puffer im Kernel-Bereich und dann vom Kernel-Bereich in den Puffer im Benutzerbereich kopiert. Im Gegensatz dazu können bei der Nullkopie Daten direkt im Kernelraum verarbeitet oder direkt vom Kernelraum auf das Netzwerkgerät übertragen werden, wodurch der CPU-Kopiervorgang reduziert, der Systemaufwand verringert und die Effizienz der Datenübertragung verbessert wird.

Quelle der Nullkopie

Das Konzept der Nullkopie tauchte erstmals im Betriebssystemdesign auf und zielte darauf ab, den Leistungsengpass zu lösen, der durch das Kopieren von Daten bei herkömmlichen E/A-Vorgängen verursacht wird. In frühen Computersystemen erforderten alle E/A-Vorgänge mehrere Datenkopien im Benutzer- und Kernelbereich. Dies wurde allmählich zu einem Leistungsengpass, nachdem Hochgeschwindigkeitsnetzwerke und Festplatten mit großer Kapazität üblich wurden.

Wichtige technische Punkte

  • Direkter I/O: Ermöglicht Anwendungen den direkten Zugriff auf Festplattengeräte und überspringt den Caching-Mechanismus des Dateisystems.
  • Speicherzuordnung (MMAP): Ordnen Sie Dateien dem Speicher zu, sodass auf Dateidaten wie auf den Speicher zugegriffen werden kann, wodurch das Kopieren reduziert wird.
  • Datei senden: Linux-Systemaufruf, der Daten direkt von einem Dateideskriptor zu einem anderen übertragen kann und so Zwischenkopien vermeidet.
  • DMA (Direkter Speicherzugriff): Technologie auf Hardwareebene, die die direkte Übertragung von Daten zwischen Gerät und Speicher ohne CPU-Beteiligung ermöglicht.

Implementierung in Java:

Java unterstützt die Zero-Copy-Technologie durch das NIO-Framework (New I/O). NIO eingeführtFileChannelUndSocketChannel und andere Klassen, die effizientere I/O-Operationen ermöglichen. Speziell,FileChannel.transferTo()UndFileChannel.transferFrom()Methoden können Daten direkt von einem Dateikanal zu einem Socket-Kanal oder umgekehrt übertragen, ohne die Daten in einen Puffer zu laden, wodurch eine Kopie ohne Null erreicht wird.

Angenommen, Sie müssen den Inhalt einer großen Datei an das Netzwerk senden. Der herkömmliche Ansatz besteht darin, zuerst den Dateiinhalt in einen Puffer zu lesen und dann den Pufferinhalt in das Netzwerk zu schreiben. Dies umfasst zwei Kopiervorgänge: einen von der Festplatte in den Puffer und einen weiteren vom Puffer in das Netzwerk.während dem BenutzentransferTo()Bei Verwendung dieser Methode können Daten direkt von der Festplatte in das Netzwerk übertragen werden, ohne dass ein Zwischenpuffer erforderlich ist, wodurch die Anzahl der Kopien reduziert und eine Nullkopie erreicht wird.

Ein Beispiel mit ByteBuffer:

ByteBufferund andereBufferKlasse (wie zCharBufferShortBufferusw.) stellen Puffer bereit, die gefüllt oder geleert werden können, ohne dass direkt eine Kopie zwischen Benutzerraum und Kernelraum erforderlich ist.ByteBufferDirekt oder indirekt in der Zero-Copy-Technologie einsetzbar:

  1. Direkter PufferByteBuffer.allocateDirect(size)ErstelltByteBuffer Instanzen werden unter Umgehung des Java-Heaps direkt dem physischen Speicher zugeordnet.Wenn ein solcher Puffer verglichen wird mitFileChanneloderSocketChannel Bei gemeinsamer Verwendung können Daten direkt zwischen physischem Speicher und Hardwaregeräten übertragen werden, ohne dass zusätzliche Kopien über den Java-Heap erforderlich sind. Dies ermöglicht auf einigen Plattformen echtes Zero-Copy.
  2. verwendenFileChannelvontransferTo()UndtransferFrom(): Mit diesen Methoden können Daten direkt gespeichert werdenFileChannelUndSocketChannelzwischen übertragenByteBuffer als Vermittler. Das bedeutet, dass Daten direkt von der Festplatte ins Netzwerk oder umgekehrt übertragen werden können, ohne dass sie zwischen Userspace und Kernelspace kopiert werden müssen.
  3. verwendenByteBuffervonwrap()Methodewrap()Mit der Methode können Sie ein vorhandenes Byte-Array oder einen anderen Puffer in einen einbindenByteBuffer , sodass die Daten nicht in einen neuen Puffer kopiert werden müssen. Dies ist nützlich, um unnötiges Kopieren von Daten zu vermeiden.
  4. verwendenByteBuffervonslice()Methodeslice() Die Methode erstellt eine Ansicht des Puffers, ohne die zugrunde liegenden Daten zu kopieren.Dies bedeutet, dass eine großeByteBufferAufteilen in mehrere kleinere Puffer, ohne Daten zu kopieren.

Verwenden Sie ByteBuffer mit FileChannel:

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class ZeroCopyExample {
    public static void main(String[] args) {
        Path path = Path.of("example.txt");
        try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
            long transferred = fileChannel.transferTo(0, fileChannel.size(), System.out);
            System.out.println("Transferred bytes: " + transferred);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Beachten:

Wir sollten beachten, dass es tatsächlich keine vollständige Nullkopie gibt. Die hier erwähnte Nullkopie gilt nur für unsere Anwendung selbst, die keine Kopie auf Benutzerebene hat. Aber selbst auf Benutzerebene ist es unmöglich, überhaupt keine Kopiervorgänge durchzuführen, sondern die Kopien so weit wie möglich zu reduzieren. Daher können wir verstehen, dass sich der Begriff Nullkopie tatsächlich auf die Technologie zur Reduzierung der Anzahl der Datenkopien bezieht. und das bedeutet nicht, dass es keinen echten Kopiervorgang gibt.