Javaの-NIOおよびチャネルバッファ

NIO:同期のノンブロッキングI / Oモデルだけでなく、多重化IのOの/の基礎。

同期および非同期

  • 同期:呼び出し側が未処理の完成を要求する前に、コールを開始した後、コールは戻りません。
  • 非同期:コールを開始した後、呼び出し側はすぐに応答が要求を受信したことを示しますが、呼び出し元は、このような通知するためのコールバックメカニズムとして、呼び出し側は通常、イベントに依存している、我々は他の要求に対処することができ、結果が返されませんでした呼び出し側は結果を返します。

呼び出し側は、発信者が結果を返す、発信者のコールバックメカニズムで通知されます、結果を待つ必要がない場合は、同期と非同期の最大の違いは非同期です。

ブロッキングとノンブロッキング

  • ブロッキング:要求を開始、呼び出し側は状況が継続する準備ができている場合にのみ、つまり、現在のスレッドが他のタスクに従事することができない、中断され、結果を返すように要求を待っています。
  • ノンブロッキング:要求を開始し、呼び出し側は返さない結果を待っている、あなたは他のことを行う行くことができます。

人生簡単な例では、あなたのお母さんは、あなたが水を沸騰あなたは水を待つだけさベナ、(同期ブロック)を比較子をみましょう。

そして、あなたは少し育つのを待つ、あなたはギャップが他の何かをやってたびを沸かすことができ、その後、水だけが開かれていないかどうかを確認するために時々必要知っている(同期非ブロッキング)。

その後、オープンウォータージャグ上のあなたの家が鳴り、あなたはちょうどあなたが簡単に自分のことを行うことができ、その間のオープン水の音を、聞いた後、知っておく必要があるので、あなたは(非非同期を注ぐ必要がありますブロッキング)。

 

、バッファー(緩衝液)

JavaのNIO内のデータへのアクセスを担当。バッファが配列です。異なるデータ型のデータを格納します。

/ * 
 *(ブールを除く)は、異なるデータ・タイプを、対応するバッファ・タイプを提供する:
 *のByteBuffer 
 * CharBufferの
 * ShortBuffer 
 * IntBuffer 
 * LongBuffer 
 * FloatBuffer 
 *のDoubleBuffer 
 * 
 上記*バッファ管理を割り当てる()により得られた、ほぼ同じですバッファ
 * /

1.基本プロパティ

  • キャパシティ(容量):バッファは、最大データ容量を表し、バッファ容量が負でないことができ、作成後に変更することはできません。
  • リミット(限界):最初の読み取りまたは書き込みは、インデックスデータ、すなわち、データが書き込み制限の後に配置することができないべきではありません。バッファが負であることができない制限、その容量より大きくすることはできません。
  • 位置(ポジション):次のインデックスデータが読み書きされます。バッファロケーションは負にすることはできません、そして限界を超えることはできません。
  • マーク(マーク)とリセット(リセット):この位置にreset()メソッドを呼び出すことによって復元することができ、マークによってバッファバッファの特定の位置を指定するためのマーカーインデックス()メソッドです。
  • マーカー位置の制限、同じ式に準拠する能力:0 <=マーク<=位置<=リミット<=容量

2.一般的な方法

すべてのサブクラスをバッファデータ操作のための2つのメソッドを提供します()を取得し(入れ) 
バッファ内のデータを取得します。

  • ()を取得:1バイトを読んで
  • (バイト[] DST)を得る:DSTのバルク内に読み込まれたバイトの複数
  • 読んでバイト指定されたインデックス(位置を移動しません):(int型のインデックス)を取得

バッファにデータを入れて

  • 置く(バイトb):現在の位置でバッファにバイトを注文します
  • 置く(バイト[] SRC):バイト書き込みバッファ内の現在の位置SRC
  • (int型のインデックス、バイトb)に置く:バッファ(移動していない位置)の指定されたバイトインデックスを書き込み

その他の方法

  • バッファクリア()、本当に限界が容量に調整されている間だけ、位置0に移動し、バッファ内のデータを削除し、-1に設定されたマークを、バッファを空にし、バッファへの参照を返します。
  • バッファフリップ()バッファ制限セットは、-1に設定されたマークの位置であり、位置は0に設定されています。

3.ダイレクトおよび非ダイレクトバッファのバッファ

// バッファ割り当て:JVMメモリ 
のByteBuffer BUF = ByteBuffer.allocateDirect(1024 );
 // 直接バッファを割り当てる:ローカルメモリ 
のByteBuffer bufDirect = ByteBuffer.allocateDirect(1024 );
 // 直接バッファか
のSystem.out。 println(buf.isDirect()); 

// 直接バイトバッファは、直接ファイル領域のFileChannelによってメモリを作成するための地図()メソッドをマッピングすることができます。この方法はれるMappedByteBufferを返します。

非ダイレクトバッファ

直接バッファ

使用するのは簡単4。

import org.junit.Test;

import java.nio.ByteBuffer;

public class TestBuffer {
    @Test
    public void markAndReset() {
        String str = "abcde14693090";
        // 分配直接缓冲区
        ByteBuffer buf = ByteBuffer.allocateDirect(1024);

        // 存入数据
        buf.put(str.getBytes());
        // 切换到读取模式
        buf.flip();

        byte[] dst = new byte[buf.limit()];
        buf.get(dst, 0, 2);
        System.out.println(new String(dst, 0, 2));
        System.out.println(buf.position());

        // mark() : 标记
        buf.mark();

        buf.get(dst, 2, 2);
        System.out.println(new String(dst, 2, 2));
        System.out.println(buf.position());

        // reset() : 恢复到 mark 的位置
        buf.reset();
        System.out.println(buf.position());

        // 判断缓冲区中是否还有剩余数据
        if (buf.hasRemaining()) {
            // 获取缓冲区中可以操作的数量
            System.out.println(buf.remaining());
        }
    }

    @Test
    public void getAndPut() {
        String str = "abcde";

        //1. 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        System.out.println("allocate():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        //2. 利用 put() 存入数据到缓冲区中
        buf.put(str.getBytes());

        System.out.println("put():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        //3. 切换读取数据模式
        buf.flip();

        System.out.println("flip():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        //4. 利用 get() 读取缓冲区中的数据
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst, 0, dst.length));

        System.out.println("get():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        //5. rewind() : 可重复读
        buf.rewind();

        System.out.println("rewind():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,只是处于“被遗忘”状态
        buf.clear();

        System.out.println("clear():" + buf.position() + "\t" + buf.limit() + "\t" + buf.capacity());

        // 获取单个字符
        System.out.println((char) buf.get());
    }
}
View Code

 

二、通道(Channel)

表示 IO 源与目标节点打开的连接,在 Java NIO 中负责缓冲区中数据的传输,类似于传统的“流”。只不过 Channel 本身不能直接访问数据,只能与 Buffer 进行交互。

/*
 * java.nio.channels.Channel 接口的主要实现类:
 *         |--FileChannel:用于读取、写入、映射和操作文件的通道。
 *         |--SocketChannel:通过 TCP 读写网络中的数据。
 *         |--DatagramChannel:通过 UDP 读写网络中的数据通道。
 *         |--ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
 */

1.获取通道

/*
 * 获取通道的一种方式是对支持通道的对象调用 getChannel() 方法。
 * 支持通道的类如下:
 * 本地 IO:
 * |--FileInputStream
 * |--FileOutputStream
 * |--RandomAccessFile
 * 网络 IO:
 * |--Socket
 * |--ServerSocket
 * |--DatagramSocket
 *
 * 在 JDK 1.7 中, NIO.2 针对各个通道的实现类提供了静态方法 open() 来获取通道。
 * 在 JDK 1.7 中, NIO.2 的 Files 工具类的静态方法 newByteChannel() 也可以获取通道。
 */

2.通道数据传输

/*
 * 将 Buffer 中数据写入 Channel
 * int bytesWritten = inChannel,write(buf)
 * 从 Channel 读取数据到 Buffer
 * int bytesRead = inChannel.read(buf)
 * 
 * Channel 之间的数据传输(将数据从源通道传输到其他 Channel 中)
 * transferFrom()
 * transferTo()
 */

复制文件的几种方式

// 通道之间的数据传输(直接缓冲区)
@Test
public void channelCopy() throws IOException {
    FileChannel inChannel = FileChannel.open(Paths.get("D:/123.txt"), StandardOpenOption.READ);
    FileChannel outChannel = FileChannel.open(Paths.get("D:/456.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

    // inChannel.transferTo(0, inChannel.size(), outChannel);
    outChannel.transferFrom(inChannel, 0, inChannel.size());

    inChannel.close();
    outChannel.close();
}

// 使用直接缓冲区完成文件的复制(内存映射文件)
@Test
public void byteBuffCopy() {
    long start = System.currentTimeMillis();

    try (FileChannel inChannel = FileChannel.open(Paths.get("D:/123.txt"), StandardOpenOption.READ);
         FileChannel outChannel = FileChannel.open(Paths.get("D:/456.txt"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE)) {

        // 内存映射文件
        MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

        // 直接对缓冲区进行数据的读写操作
        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);
    } catch (IOException e) {
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();
    System.out.println("耗费时间为:" + (end - start));
}

// 利用通道完成文件的复制(非直接缓冲区)
@Test
public void fileCopy() {
    long start = System.currentTimeMillis();

    try (FileInputStream fis = new FileInputStream("D:/123.txt");
         FileOutputStream fos = new FileOutputStream("D:/456.txt");
         // 获取通道
         FileChannel inChannel = fis.getChannel();
         FileChannel outChannel = fos.getChannel()) {

        // 分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        // 将通道中的数据存入缓冲区中
        while (inChannel.read(buf) != -1) {
            // 切换读取数据的模式
            buf.flip();
            // 将缓冲区中的数据写入通道中
            outChannel.write(buf);
            // 清空缓冲区
            buf.clear();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    long end = System.currentTimeMillis();
    System.out.println("Time:" + (end - start));
}
View Code

3.分散(Scatter)与聚集(Gather)

分散读取(Scattering Reads):从 Channel 中读取的数据“分散”到多个 Buffer 中。(按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。)

聚集写入(Gathering Writes):将多个 Buffer 中的数据“聚集”到 Channel。(按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 。)

使用分散和聚集来复制文件部分内容

// 分散和聚集
@Test
public void scatterAndGather() throws IOException {
    RandomAccessFile raf1 = new RandomAccessFile("D:/123.txt", "rw");

    // 获取通道
    FileChannel channel1 = raf1.getChannel();
    // 分配指定大小的缓冲区
    ByteBuffer buf1 = ByteBuffer.allocate(100);
    ByteBuffer buf2 = ByteBuffer.allocate(1024);
    // 分散读取
    ByteBuffer[] bufs = {buf1, buf2};
    channel1.read(bufs);

    // 转换模式
    for (ByteBuffer byteBuffer : bufs) {
        byteBuffer.flip();
    }

    System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
    System.out.println("-----------------");
    System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));

    // 聚集写入
    RandomAccessFile raf2 = new RandomAccessFile("D:/456.txt", "rw");
    FileChannel channel2 = raf2.getChannel();
    channel2.write(bufs);
}
View Code

4.字符集

/*
 * 字符集:Charset
 * 编码:字符串 -> 字节数组
 * 解码:字节数组  -> 字符串
 */
@Test
public void testCharset() throws IOException {
    // 指定字符集
    Charset cs1 = Charset.forName("GBK");

    CharBuffer cBuf = CharBuffer.allocate(1024);
    cBuf.put("字符集");

    cBuf.flip();
    //获取编码器
    CharsetEncoder ce = cs1.newEncoder();
    //编码
    ByteBuffer bBuf = ce.encode(cBuf);
    System.out.println(Arrays.toString(bBuf.array()));


    bBuf.flip();
    //获取解码器
    CharsetDecoder cd = cs1.newDecoder();
    //解码
    cBuf = cd.decode(bBuf);
    System.out.println(cBuf.toString());
}

// Java 支持的字符集
@Test
public void getCharset() {
    Map<String, Charset> map = Charset.availableCharsets();
    for (Entry<String, Charset> entry : map.entrySet()) {
        System.out.println(entry.getKey() + "=" + entry.getValue());
    }
}

 


https://snailclimb.gitee.io/javaguide/#/java/BIO-NIO-AIO

https://snailclimb.gitee.io/javaguide/#/java/Java%20IO%E4%B8%8ENIO

https://cyc2018.github.io/CS-Notes/#/notes/Java%20IO?id=%e4%b8%83%e3%80%81nio

https://www.cnblogs.com/dolphin0520/p/3919162.html

https://ifeve.com/java-nio-all/

おすすめ

転載: www.cnblogs.com/jhxxb/p/11272727.html