ホームページ > Java > &#&はじめる > Java NIO の詳細な紹介

Java NIO の詳細な紹介

王林
リリース: 2020-10-21 16:35:21
転載
1835 人が閲覧しました

Java NIO の詳細な紹介

Java NIO は、大容量のデータ送信の効率を向上させるために、Java I/O を補足するものとして、主にバッファー、チャネル、セレクターの 3 つの中心的な概念を理解する必要があります。

(推奨チュートリアル: java コース)

NIOを学ぶ前に、基本的なネットワーク プログラミングの知識を持っておくことが最善です

Java I/O フロー

Java ネットワーク プログラミング

Java NIO: バッファ

チャネル (チャネル) は、NIO の 3 つの中心概念 (バッファ、チャネル、セレクタ) の 1 つであり、効果的な送信で使用されます。バイト バッファとチャネルの反対側のエンティティ (ファイルまたはソケット) の間のデータ (コアはデータ送信です)

NIO プログラミングの一般的なパターンは次のとおりです: データを送信ワードに埋め込む セクションバッファ --> チャネル経由でチャネル ピア ファイルまたはソケットに送信

チャネルの基本

チャネルを使用する目的は、データを送信することです。使用する前に、チャネルは次のことを行う必要があります。

#チャネルを開く

I/O には、ファイル IO とストリーム I という 2 つの主要なカテゴリがあることがわかっています。 /O に対応します。 チャネルには、ファイル チャネル (FileChannel) とソケット チャネル (SocketChannel、ServerSocketChannel、DatagramChannel) の 2 種類があります。

ソケット チャネルの場合は、静的ファクトリ メソッドを使用して開きます。
SocketChannel sc = SocketChannel.open();
ServerSocketChannel sc = ServerSocketChannel.open();
DatagramChannel sc = DatagramChannel.open();
ログイン後にコピー

ファイル チャネルの場合、RandomAccessFile、FileInputStream、および FileOutputStream オブジェクトの getChannel() メソッドを呼び出すことによってのみ取得できます。

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();
ログイン後にコピー

データ送信にはチャネルを使用します

#次のコードは、最初に書き込まれるデータを ByteBuffer に配置し、次にファイル チャネルを開いて、バッファ内のデータをファイル チャネルに配置します。

//准备数据并放入字节缓冲区
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();
ログイン後にコピー

チャネルを閉じる

Socket、FileInputStream、その他のオブジェクトを使用後に閉じる必要があるのと同様に、チャネルも使用後に閉じる必要があります。オープン チャネルは、特定の I/O サービスへの特定の接続を表し、その接続の状態をカプセル化します。チャネルが閉じると、接続は失われ、何も接続されなくなります。

ブロッキング モードとノンブロッキング モード

チャネルには、ブロッキングとノンブロッキングの 2 つの動作モードがあります。ノンブロッキング モードのチャネルは決してスリープしません。要求された操作はすぐに完了するか、操作が実行されていないことを示す結果を返します (詳細については、ソケット チャネルの説明を参照してください)。ストリーム指向のチャネルのみがノンブロッキング モードを使用できます

ファイル チャネル

ファイル チャネルは、RandomAccessFile、FileInputStream、FileOutputStream オブジェクトで getChannel を呼び出すことによってファイルにアクセスするために使用されます ( )の入手方法です。 getChannel メソッドを呼び出すと、ファイル オブジェクトと同じアクセス権を持つ同じファイルに接続された FileChannel オブジェクトが返されます。

ファイル アクセス

ファイル チャネルを使用する目的は、ファイルの読み取りと書き込みです。チャネルの読み取りおよび書き込み API は次のとおりです:

public abstract int read(ByteBuffer dst) throws IOException;
public abstract int write(ByteBuffer src) throws IOException;
ログイン後にコピー

次はファイル読み取りのデモの段落です

//打开文件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());
ログイン後にコピー

上のデモには問題があります: バイトのみを読み取ることができ、アプリケーションにそれらをデコードさせます。この問題には、次のツールを使用できます。クラス Channels を使用して、チャネルを Reader と Writer にラップします。解決策; もちろん、Java I/O ストリーム モードの Reader と Writer を直接使用して文字を操作することもできます。

ファイル チャネルの位置とファイル ホール

ファイル チャネルの位置 (position) 通常のファイルの位置です。position の値によって、ファイル内のどの位置が次に読み書きされるかが決まります。

以降のデータの読み取りファイルの終わりは -1 (ファイル EOF) を返します

ファイルの終わりを超える位置にデータを書き込むと、ファイル ホールが発生します。たとえば、ファイルには現在 10 バイトがありますが、その位置にデータを書き込むと、ファイル ホールが発生します。 =20 を指定すると、10 と 20 の間の位置が空になります。データ、これはファイル ホールです

強制操作

強制操作により、チャネルが直ちに適用されます。ディスク ファイルへのすべての変更 (システムのダウンタイムや変更の損失を防ぐため)

public abstract void force(boolean metaData) throws IOException;
ログイン後にコピー

メモリ ファイル マッピング

FileChannel には、仮想ファイル マッピングを確立できる map() メソッドが用意されています。開いているファイルと特殊なタイプの ByteBuffer (MappedByteBuffer) メモリ マップ間の接続。

map メソッドによって返される MappedByteBuffer オブジェクトはダイレクト バッファであるため、MappedByteBuffer を介してファイルを操作するのは非常に効率的です (特に大量のデータ転送の場合)

MappedByteBuffer の使用

MappedByteBuffer によるファイルの読み取り

FileInputStream in = new FileInputStream("/tmp/a.txt");
FileChannel fc = in.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_ONLY, 0, fc.size());
StringBuilder builder = new StringBuilder();
while (mbb.hasRemaining()) {
  builder.append((char) mbb.get());
}
System.out.println(builder.toString());
ログイン後にコピー

MappedByteBuffer の 3 つのモード

READ_ONLY

READ_WRITE

PRIVATE

読み取り専用モードと読み取り/書き込みモードは理解しやすいです。PRIVATE モードでは、書き込み操作によって一時バッファーが書き込まれ、実際にはファイルが書き込まれません。 (コピーオンライトのアイデア)

ソケット チャネル

ソケット チャネルはノンブロッキング モードで実行でき、オプションです。この 2 つの点により、もう考えなくなります。ネットワーク プログラミング: ソケット接続ごとにスレッドを作成する必要がありますが、数百または数千のソケット接続を管理するには単一のスレッドを使用します。

すべてのソケット チャネルは、インスタンス化されるときにオブジェクトのソケット オブジェクトを作成します。ソケット チャネルは、プロトコル関連の操作を担当しません。プロトコル関連の操作は、ピア ソケット オブジェクト (SocketChannel オブジェクトなど) に委任されます。 Socket オブジェクトに委任されます)

非阻塞模式

相较于传统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;
ログイン後にコピー

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);
  }
}
ログイン後にコピー

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);
ログイン後にコピー

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

    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]);
  }
ログイン後にコピー

总结

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

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

相关推荐:java入门

以上がJava NIO の詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート