Heim > Java > JavaErste Schritte > Hauptteil

Eine ausführliche Einführung in Java NIO

王林
Freigeben: 2020-10-21 16:35:21
nach vorne
1753 Leute haben es durchsucht

Eine ausführliche Einführung in Java NIO

Java NIO muss hauptsächlich die drei Kernkonzepte Puffer, Kanäle und Selektoren als Ergänzung zu Java I/O verstehen, um die Effizienz der Übertragung großer Datenmengen zu verbessern.

(Empfohlenes Tutorial: Java-Kurs)

Bevor Sie NIO lernen, ist es am besten, über grundlegende Netzwerkprogrammierkenntnisse zu verfügen Als eines der drei Kernkonzepte von NIO (Puffer, Kanal, Selektor) wird es verwendet, um Daten effektiv zwischen dem Bytepuffer und der Entität (Datei oder Socket) auf der anderen Seite des Kanals zu übertragen (der Kern ist die Übertragung von Daten)

Der allgemeine Modus der NIO-Programmierung ist: Füllen Sie die Daten in den Sendebyte-Puffer--> Senden Sie sie über den Kanal an die Kanal-Peer-Datei oder den Socket.

Grundlagen des Kanals. Der Kanal muss vor der Verwendung geöffnet und nach der Verwendung geschlossen werden

Öffnen Sie den Kanal

Wir wissen, dass es zwei Hauptkategorien von E/A gibt: Datei-E/A und Stream-E/A und entsprechend Für den Kanal gibt es auch einen Dateikanal (FileChannel) und einen Socket-Kanal (SocketChannel, ServerSocketChannel, DatagramChannel)

Für den Socket-Kanal verwenden Sie zum Öffnen die statische Factory-Methode

SocketChannel sc = SocketChannel.open();
ServerSocketChannel sc = ServerSocketChannel.open();
DatagramChannel sc = DatagramChannel.open();
Nach dem Login kopieren

Für den Dateikanal können Sie nur getChannel( aufrufen () auf einem RandomAccessFile-, FileInputStream-, FileOutputStream-Objekt), um

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();
Nach dem Login kopieren

Kanäle für die Datenübertragung verwenden

zu erhalten. Der folgende Code fügt zuerst die zu schreibenden Daten in den ByteBuffer ein, öffnet dann den Dateikanal und fügt die Daten ein den Puffer in den Dateikanal.

//准备数据并放入字节缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
bf.put("i am cool".getBytes());
bf.flip();
//打开文件通道
FileOutputStream out = new FileOutputStream("/tmp/a.txt");
FileChannel fc = out.getChannel();
//数据传输
fc.write(bf);
//关闭通道
fc.close();
Nach dem Login kopieren

Schließen Sie den Kanal

So wie Socket, FileInputStream und andere Objekte nach der Verwendung geschlossen werden müssen, muss auch der Kanal nach der Verwendung geschlossen werden. Ein offener Kanal stellt eine bestimmte Verbindung zu einem bestimmten E/A-Dienst dar und kapselt den Status dieser Verbindung. Wenn der Kanal geschlossen ist, geht die Verbindung verloren und es ist nichts damit verbunden.

Blockierender und nicht blockierender Modus

Kanäle haben zwei Betriebsmodi: blockierend und nicht blockierend. Kanäle im nicht blockierenden Modus werden entweder sofort abgeschlossen oder es wird ein Ergebnis zurückgegeben, das dies anzeigt Es wurde keine Operation ausgeführt (siehe insbesondere die Beschreibung des Socket-Kanals). Nur Stream-orientierte Kanäle können den nicht blockierenden Modus verwenden

Dateikanäle

Dateikanäle werden für den Zugriff auf Dateien verwendet und durch Aufrufen der getChannel()-Methode für ein RandomAccessFile-, FileInputStream- und FileOutputStream-Objekt abgerufen. Der Aufruf der getChannel-Methode gibt ein FileChannel-Objekt zurück, das mit derselben Datei verbunden ist und über dieselben Zugriffsrechte wie das Dateiobjekt verfügt.

Dateizugriff

Der Zweck der Verwendung von Dateikanälen besteht darin, Dateien zu lesen und zu schreiben. Die Lese- und Schreib-API des Kanals lautet wie folgt:

public abstract int read(ByteBuffer dst) throws IOException;
public abstract int write(ByteBuffer src) throws IOException;
Nach dem Login kopieren
Das Folgende ist eine Demo zum Lesen von Dateien
//打开文件channel
RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
FileChannel fc = f.getChannel();
//从channel中读取数据,直到文件尾
ByteBuffer bb = ByteBuffer.allocate(1024);
while (fc.read(bb) != -1) {
;
}
//翻转(读之前需要先进行翻转)
bb.flip();
StringBuilder builder = new StringBuilder();
//把每一个字节转为字符(ascii编码)
while (bb.hasRemaining()) {
builder.append((char) bb.get());
}
System.out.println(builder.toString());
Nach dem Login kopieren

Es gibt eine Problem mit der obigen Demo: Wir können Bytes nur lesen und dann von der Anwendung dekodieren. Wir können dieses Problem lösen, indem wir den Kanal über die Tool-Klasse Kanäle packen Writer des Java-I/O-Stream-Modus zum Betreiben von Zeichen.

Dateikanalposition und Dateiloch

Die Position des Dateikanals ist die Position einer gewöhnlichen Datei Als nächstes lesen oder schreiben

Über das Ende der Datei hinaus lesen Die Daten geben -1 zurück (Datei-EOF)

Das Schreiben von Daten an eine Position hinter dem Ende der Datei führt zu einer Dateilücke: Beispielsweise hat eine Datei derzeit 10 Bytes, aber das Schreiben von Daten an Position = 20 führt zu diesem Zeitpunkt dazu, dass an den Positionen zwischen 10 und 20 keine Daten vorhanden sind. Dies ist eine Dateilücke Wenden Sie alle Änderungen sofort auf die Festplattendatei an (um Systemausfallzeiten und Änderungsverluste zu vermeiden). von ByteBuffer (MappedByteBuffer).

Da das von der Map-Methode zurückgegebene MappedByteBuffer-Objekt ein direkter Puffer ist, ist es sehr effizient, Dateien über MappedByteBuffer zu verwalten (insbesondere bei großen Datenmengen).

Verwendung von MappedByteBuffer

Dateien über MappedByteBuffer lesen

public abstract void force(boolean metaData) throws IOException;
Nach dem Login kopieren

MappedByteBuffer Die drei Modi

READ_ONLY

READ_WRITE

PRIVATE

read-only- und read-write-Modi sind leicht zu verstehen. Im PRIVATE-Modus schreibt der Schreibvorgang einen temporären Puffer und schreibt den nicht wirklich Datei. (Copy-on-Write-Idee)

Socket-Kanal

Socket-Kanal kann im nicht blockierenden Modus ausgeführt werden und ist optional. Aufgrund dieser beiden Punkte müssen wir für die Netzwerkprogrammierung nicht mehr einen Thread für jede Socket-Verbindung erstellen Verwenden Sie einen Thread, um Hunderte oder Tausende von Socket-Verbindungen zu verwalten.

Alle Socket-Kanäle erstellen bei der Instanziierung ein Socket-Objekt eines Objekts. Protokollbezogene Vorgänge werden nicht an Peer-Socket-Objekte delegiert (z. B. SocketChannel-Objekte werden an Socket-Objekte delegiert).

非阻塞模式

相较于传统Java Socket的阻塞模式,SocketChannel提供了非阻塞模式,以构建高性能的网络应用程序

非阻塞模式下,几乎所有的操作都是立刻返回的。比如下面的SocketChannel运行在非阻塞模式下,connect操作会立即返回,如果success为true代表连接已经建立成功了, 如果success为false, 代表连接还在建立中(tcp连接需要一些时间)。

 //打开Socket通道
 SocketChannel ch = SocketChannel.open();
 //非阻塞模式
 ch.configureBlocking(false);
 //连接服务器 
 boolean success = ch.connect(InetSocketAddress.createUnresolved("127.0.0.1", 7001));
 //轮训连接状态, 如果连接还未建立就可以做一些别的工作
 while (!ch.finishConnect()){
    //dosomething else
 }
 //连接建立, 做正事
 //do something;
Nach dem Login kopieren

ServerSocketChannel

ServerSocketChannel与ServerSocket类似,只是可以运行在非阻塞模式下

下为一个通过ServerSocketChannel构建服务器的简单例子,主要体现了非阻塞模式,核心思想与ServerSocket类似

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(7001));
while (true){
  SocketChannel sc = ssc.accept();
  if(sc != null){
    handle(sc);
  }else {
    Thread.sleep(1000);
  }
}
Nach dem Login kopieren

SocketChannel 与 DatagramChannel

SocketChannel 对应 Socket, 模拟TCP协议;DatagramChannel对应DatagramSocket, 模拟UDP协议

二者的使用与SeverSocketChannel大同小异,看API即可

工具类

文体通道那里我们提到过, 通过只能操作字节缓冲区, 编解码需要应用程序自己实现。如果我们想在通道上直接操作字符,我们就需要使用工具类Channels,工具类Channels提供了通道与流互相转换、通道转换为阅读器书写器的能力,具体API入下

//通道 --> 输入输出流
public static OutputStream newOutputStream(final WritableByteChannel ch);
public static InputStream newInputStream(final AsynchronousByteChannel ch);
//输入输出流 --> 通道
public static ReadableByteChannel newChannel(final InputStream in);
public static WritableByteChannel newChannel(final OutputStream out);
//通道  --> 阅读器书写器
public static Reader newReader(ReadableByteChannel ch, String csName);
public static Writer newWriter(WritableByteChannel ch, String csName);
Nach dem Login kopieren

通过将通道转换为阅读器、书写器我们就可以直接在通道上操作字符。

    RandomAccessFile f = new RandomAccessFile("/tmp/a.txt", "r");
  FileChannel fc = f.getChannel();
  //通道转换为阅读器,UTF-8编码
  Reader reader = Channels.newReader(fc, "UTF-8");
  int i = 0, s = 0;
  char[] buff = new char[1024];
  while ((i = reader.read(buff, s, 1024 - s)) != -1) {
    s += i;
  }
  for (i = 0; i < s; i++) {
    System.out.print(buff[i]);
  }
Nach dem Login kopieren

总结

通道主要分为文件通道和套接字通道。

对于文件操作:如果是大文件使用通道的文件内存映射特性(MappedByteBuffer)来有利于提升传输性能, 否则我更倾向传统的I/O流模式(字符API);对于套接字操作, 使用通道可以运行在非阻塞模式并且是可选择的,利于构建高性能网络应用程序。

相关推荐:java入门

Das obige ist der detaillierte Inhalt vonEine ausführliche Einführung in Java NIO. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:juejin.im
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!