Heim  >  Artikel  >  Java  >  Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen

王林
王林nach vorne
2023-05-06 22:25:06806Durchsuche

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen


1. Java Mind Map

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen

# 🎜🎜#2. I/O-Modell

Der Kern des I/O-Modells besteht darin, welche Art von Kanal zum Senden und Empfangen von Daten verwendet wird, was weitgehend die Leistung der Programmkommunikation bestimmt.

Java unterstützt drei Netzwerkprogrammierungsmodelle: BIO, NIO, AIO

  • BIO: Synchronisation und Blockierung, der Dienstimplementierungsmodus ist eine Verbindung und ein Thread Wenn der Client eine Verbindungsanforderung hat, muss der Server einen Thread zur Verarbeitung starten.

  • NIO: Synchron und nicht blockierend. Der Serverimplementierungsmodus ist ein Thread, der mehrere Anforderungsverbindungen verarbeitet, dh vom Client gesendete Anforderungen werden im Multiplexer registriert , Der Multiplexer fragt die Verbindung nach I/O-Anfragen ab und verarbeitet diese.

  • AIO: Asynchrones Nichtblockieren, AIO führt das Konzept des asynchronen Kanals ein, übernimmt den Proactor-Modus, vereinfacht das Schreiben von Programmen und startet Threads erst nach gültigen Anforderungen Das Betriebssystem führt den Vorgang zunächst aus und benachrichtigt dann den Server.

3. BIO-, NIO-, AIO-Anwendungsszenarien

  • Die BIO-Methode eignet sich für eine kleine Anzahl von Verbindungen und Feste Architektur, diese Methode stellt relativ hohe Anforderungen an Serverressourcen und die Parallelität ist auf Anwendungen beschränkt. Dies war die einzige Wahl vor JDK1.4, aber das Programm ist einfach und leicht zu verstehen.

  • Die NIO-Methode eignet sich für Architekturen mit einer großen Anzahl von Verbindungen und relativ kurzen Verbindungen (leichte Operationen), wie z. B. Chat-Server, Sperrsysteme, Kommunikation zwischen Servern , usw. Die Programmierung ist komplizierter und JDK1.4 beginnt, sie zu unterstützen.

  • Die AIO-Methode wird in Architekturen mit einer großen Anzahl von Verbindungen und relativ langen Verbindungen (schwere Vorgänge) verwendet, wie z. B. Fotoalbumservern, die das Betriebssystem vollständig aufrufen Nehmen Sie an gleichzeitigen Vorgängen teil und die Programmierung ist relativ komplex. JDK7 beginnt mit der Unterstützung von

4. Einfacher BIO-Programmierungsprozess

    # 🎜🎜#Starten Sie einen ServerSocket auf der Serverseite;
  • Der Client startet Socket, um mit dem Server zu kommunizieren um mit ihm zu kommunizieren;
  • Nachdem der Client eine Anfrage gesendet hat, konsultiert er zunächst den Server, um festzustellen, ob eine Thread-Antwort vorliegt abgelehnt;

  • Wenn eine Antwort vorliegt, wartet der Client auf das Ende der Anforderung, bevor er mit der Ausführung fortfährt. .

    NIO ist eine pufferorientierte oder blockorientierte Programmierung. Die Daten werden in einen Puffer eingelesen und können bei Bedarf im Puffer hin und her verschoben werden, was die Flexibilität im Verarbeitungsprozess erhöht . Verwenden Sie es, um ein nicht blockierendes, hoch skalierbares Netzwerk bereitzustellen.
  • HTTP2.0 verwendet Multiplexing-Technologie, um zu ermöglichen, dass dieselbe Verbindung mehrere Anforderungen gleichzeitig verarbeitet, und die Anzahl gleichzeitiger Anforderungen ist um mehrere Größenordnungen höher als bei HTTP1.1.
  • Kurz gesagt: NIO kann mehrere Anfragen mit einem Thread bearbeiten.

    6. Vergleich zwischen BIO und NIO

BIO verarbeitet Daten in einem Stream, während NIO Daten in einem Block verarbeitet, Block I/The Die Effizienz von O ist viel höher als die von Stream-I/O; Stream und Zeichenstrom, während NIO auf der Grundlage von Kanal und Puffer arbeitet. Daten werden immer vom Kanal in den Puffer gelesen oder vom Puffer in den Kanal geschrieben. Der Selektor wird zum Überwachen mehrerer Kanalereignisse (z. B. Verbindungsanforderungen, Datenankunft usw.) verwendet, sodass ein einzelner Thread mehrere Clientkanäle überwachen kann.




7. Schematische Darstellung der drei Kernprinzipien von NIO

    Flussdiagrammbeschreibung:
  • # 🎜 🎜#

    Selektor entspricht einem Thread und ein Thread entspricht mehreren Kanälen (Verbindungen); drei Kanäle, die im Selektor //Programm registriert sind;
  • Jeder Kanal entspricht einem Puffer;
  • Welches Programm auf den Kanal umgeschaltet wird, hängt von den Ereignissen ab, und das Ereignis ist ein wichtiges Konzept; 🎜🎜 #
Puffer ist ein Speicherblock und unten befindet sich ein Array.

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-BeispielenDaten werden über den Puffer gelesen und geschrieben. this und BIO , BIO ist entweder ein Eingabestream oder ein Ausgabestream, der nicht bidirektional sein kann, aber der Puffer von NIO kann gelesen oder geschrieben werden und erfordert einen Flip-Methodenwechsel; #channel ist bidirektional, Sie können den Status des zugrunde liegenden Betriebssystems zurückgeben, z. B. Linux, der zugrunde liegende Betriebssystemkanal ist bidirektional
;

8. Puffer

Ein Puffer ist im Wesentlichen ein Speicherblock, der Daten lesen und schreiben kann. Er kann als Containerobjekt (einschließlich eines Arrays) verstanden werden. Dieses Objekt stellt eine Reihe von Methoden bereit, die die Verwendung erleichtern können Speicherblöcke und Pufferobjekte verfügen über integrierte Mechanismen, die Änderungen des Pufferzustands verfolgen und aufzeichnen können. Der Kanal stellt einen Kanal zum Lesen von Daten aus Dateien und Netzwerken bereit, die gelesenen oder geschriebenen Daten müssen jedoch über den Puffer erfolgen.
In NIO ist Buffer eine übergeordnete Klasse der obersten Ebene, bei der es sich um eine abstrakte Klasse handelt.

1. Liste häufig verwendeter Puffer-Unterklassen

  • ByteBuffer, speichert Byte-Daten im Puffer;

  • ShortBuffer, speichert Zeichendaten im Puffer Area;

  • IntBuffer, speichert Ganzzahldaten im Puffer;

  • DoubleBuffer, speichert Dezimalzahlen im Puffer; s zu der Puffer;

  • 2. Vier Hauptattribute des Puffers
  • Markierung: Markierung
  • Position: der Index des nächsten Elements, das jedes Mal gelesen oder geschrieben werden soll gelesen oder geschrieben Der Wert wird jedes Mal geändert, um ihn auf das nächste Lesen und Schreiben vorzubereiten.

Limit: Gibt den aktuellen Endpunkt des Puffers an. Lese- und Schreibvorgänge können nicht an Positionen ausgeführt werden, an denen der Puffer das Limit überschreitet. Und das Limit kann geändert werden

  • Kapazität: Kapazität, also die maximale Datenmenge, die aufgenommen werden kann; sie wird beim Erstellen des Puffers festgelegt und kann nicht geändert werden.

  • 3. Die API wurde eingeführt, als die häufig verwendete Puffer-API

  • JDK1.4

public final int Capacity( )//Gibt die Kapazität dieses Puffers zurück

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen

public final int position ( )//Die Position dieses Puffers zurückgeben

public final Buffer position (int newPositio)//Die Position dieses Puffers festlegen

  • public final int limit( )//Die Grenze dieses Puffers zurückgeben

  • public final Buffer limit (int newLimit)//Legen Sie die Grenze dieses Puffers fest

  • public final Buffer mark( )//Setzen Sie eine Markierung an der Position dieses Puffers

  • public final Buffer reset( ) // Setzen Sie die Position dieses Puffers auf die Position der vorherigen Markierung zurück

  • public final Buffer clear( )//Löschen Sie diesen Puffer, d. h. stellen Sie jede Markierung auf ihren Anfangszustand wieder her, aber die Daten werden nicht wirklich gelöscht, und wird durch nachfolgende Operationen überschrieben

  • public final Buffer flip( )//Diesen Puffer umkehren

  • public final Buffer rewind( )//Diesen Puffer zurückspulen

  • public final int verbleibend( )//Zurückgeben die aktuelle Position mit Anzahl der Elemente zwischen den Grenzwerten

  • public final boolean hasRemaining( ) // Gibt an, ob Elemente zwischen der aktuellen Position und dem Grenzwert vorhanden sind

  • public abstract boolean isReadOnly( ); // Gibt an, ob dieser Puffer vorhanden ist ist ein schreibgeschützter Puffer

  • API, eingeführt in JDK1.6

  • public abstract boolean hasArray();//Informieren Sie, ob dieser Puffer über ein zugängliches zugrunde liegendes Implementierungsarray verfügt

  • public abstract Object array();/ /Gibt das zugrunde liegende Implementierungsarray dieses Puffers zurück

public abstract int arrayOffset();//Gibt den Offset des ersten Pufferelements im zugrunde liegenden Implementierungsarray dieses Puffers zurück

  • public abstract boolean isDirect( ; Lesen und Das Schreiben kann gleichzeitig ausgeführt werden, während Streams nur lesen oder schreiben können.

  • Kanäle können Daten asynchron lesen und schreiben. Kanäle können Daten aus dem Puffer lesen oder in den Puffer schreiben. 2) Der Stream in BIO ist unidirektional. Beispielsweise kann das FileInputStream-Objekt nur Daten lesen, während der Kanal in NIO bidirektional ist und lesen oder schreiben kann.

    (3) Channel ist eine Schnittstelle in NIO
  • (4) Häufig verwendete Channel-Klassen sind: FileChannel, DatagramChannel, ServerSocketChannel und SocketChannel. ServerSocketChanne ähnelt ServerSocket und SocketChannel ähnelt Socket.
  • (5) FileChannel wird zum Lesen und Schreiben von Dateidaten verwendet, DatagramChannel wird zum Lesen und Schreiben von UDP-Daten verwendet, ServerSocketChannel und SocketChannel werden zum Lesen und Schreiben von TCP-Daten verwendet.

  • 2. FileChannel
  • FileChannel wird hauptsächlich zum Ausführen von E/A-Vorgängen für lokale Dateien verwendet:

Analysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielenlesen, Daten aus dem Kanal lesen und in den Puffer legen

schreiben Puffer Die Daten im Bereich werden in den Kanal geschrieben

transferFrom, kopieren Sie die Daten vom Zielkanal auf den aktuellen Kanal

  • transferTo, kopieren Sie die Daten vom aktuellen Kanal auf den Zielkanal

3. Hinweise und Details zu Puffer und Kanal

  • ByteBuffer unterstützt typisiertes Put und Get, zum Abrufen sollte der entsprechende Datentyp verwendet werden. Andernfalls kann es zu einer BufferUnderflowException-Ausnahme kommen .

  • Sie können einen normalen Puffer in einen schreibgeschützten Puffer umwandeln.

  • NIO bietet auch MappedByteBuffer, mit dem Dateien direkt im Speicher (Speicher außerhalb des Heaps) geändert werden können. Die Synchronisierung mit Dateien wird von NIO abgeschlossen.

  • NIO unterstützt auch Lese- und Schreibvorgänge über mehrere Puffer (d. h. Pufferarrays), nämlich Scattering und Gathering.

10. Selektor(Selektor)

1. Grundlegende Einführung

  • NIO von Java unter Verwendung der nicht blockierenden IO-Methode. Sie können einen Thread verwenden, um mehrere Clientverbindungen zu verarbeiten, und Sie verwenden den Selector.

  • Selector kann erkennen, ob ein Ereignis auf mehreren registrierten Kanälen auftritt. Wenn ein Ereignis auftritt, ruft es das Ereignis ab und verarbeitet jedes Ereignis entsprechend. Auf diese Weise kann nur ein einziger Thread zur Verwaltung mehrerer Kanäle, also zur Verwaltung mehrerer Verbindungen und Anfragen, verwendet werden.

  • Nur wenn die Verbindung/der Kanal tatsächlich ein Lese- oder Schreibereignis hat, werden Lese- und Schreibvorgänge ausgeführt, wodurch der Systemaufwand erheblich reduziert wird und nicht für jede Verbindung ein Thread erstellt und mehrere Threads verwaltet werden müssen.

  • Vermeiden Sie den Overhead, der durch den Kontextwechsel zwischen mehreren Threads entsteht. ?? , fügen Sie den entsprechenden SelectionKey zur internen Sammlung hinzu und geben Sie ihn zurück. Die Parameter werden verwendet, um das Timeout festzulegen

selectedKeys(); //Alle SelectionKeys aus der internen Sammlung abrufen.

  • 3. Hinweise

  • Die ServerSocketChannel-Funktion in NIO ähnelt ServerSocket und die SocketChannel-Funktion ähnelt Socket.
  • 11. Einfache Server-Client-Kommunikation über NIO

  • 1. Client

    package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer {
        private Selector selector;
        private ServerSocketChannel serverSocketChannel;
        private static final int PORT = 8080;
    
        public NioServer() {
            try {
                //获得选择器
                selector = Selector.open();
                serverSocketChannel =  ServerSocketChannel.open();
                //绑定端口
                serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
                //设置非阻塞模式
                serverSocketChannel.configureBlocking(false);
                //将该ServerSocketChannel 注册到selector
                serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            }catch (IOException e) {
                System.out.println("NioServer error:"+e.getMessage());
            }
        }
    
        public void listen() {
    
            System.out.println("监听线程启动: " + Thread.currentThread().getName());
            try {
                while (true) {
                    int count = selector.select();
                    if(count > 0) {
                        //遍历得到selectionKey集合
                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()) {
                            SelectionKey key = iterator.next();
    
                            if(key.isAcceptable()) {
                                SocketChannel sc = serverSocketChannel.accept();
                                sc.configureBlocking(false);
                                sc.register(selector, SelectionKey.OP_READ);
                                System.out.println(sc.getRemoteAddress() + " 上线 ");
                            }
                            //通道发送read事件,即通道是可读的状态
                            if(key.isReadable()) {
                                getDataFromChannel(key);
                            }
                            //当前的key 删除,防止重复处理
                            iterator.remove();
                        }
                    } else {
                        System.out.println("等待中");
                    }
                }
            }catch (Exception e) {
                System.out.println("listen error:"+e.getMessage());
            }
        }
    
        private void getDataFromChannel(SelectionKey key) {
            SocketChannel channel = null;
            try {
                channel = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
    
                int count = channel.read(buffer);
                //根据count的值做处理
                if(count > 0) {
                    String msg = new String(buffer.array());
                    System.out.println("来自客户端: " + msg);
    
                    //向其它的客户端转发消息(排除自己)
                    sendInfoToOtherClients(msg, channel);
                }
            }catch (IOException e) {
                try {
                    System.out.println(channel.getRemoteAddress() + " 离线了");
                    //取消注册
                    key.cancel();
                }catch (IOException ex) {
                    System.out.println("getDataFromChannel error:"+ex.getMessage());
                }
            }finally {
                try {
                    channel.close();
                }catch (IOException ex) {
                    System.out.println("channel.close() error:"+ex.getMessage());
                }
            }
        }
    
        //转发消息给其它客户(通道)
        private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{
            System.out.println("服务器转发消息中...");
            System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
            //遍历 所有注册到selector 上的 SocketChannel,并排除 self
            for(SelectionKey key: selector.keys()) {
                Channel targetChannel = key.channel();
    
                //排除自己
                if(targetChannel instanceof  SocketChannel && targetChannel != self) {
                    SocketChannel dest = (SocketChannel)targetChannel;
                    //将信息存储到buffer
                    ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                    //将buffer数据写入通道
                    dest.write(buffer);
                }
            }
        }
    
        public static void main(String[] args) {
            //创建服务器对象
            NioServer nioServer = new NioServer();
            nioServer.listen();
        }}
3

Das obige ist der detaillierte Inhalt vonAnalysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen