Java-NIO and the Channel Buffer

NIO: a synchronous non-blocking I / O model, but also the basis of the multiplexed I O /.

Synchronous and asynchronous

  • Synchronization: After initiating a call, before the caller requests untreated finished, the call does not return.
  • Asynchronous: After initiating a call, the caller immediately get a response indicates request has been received, but the caller did not return results, then we can deal with other requests, the caller usually rely on the event, such as a callback mechanism to notify The caller returns the result.

The biggest difference between synchronous and asynchronous is asynchronous if the caller does not need to wait for the results, the caller will be notified by the caller callback mechanisms that return results.

Blocking and non-blocking

  • Blocking: initiating a request, the caller has been waiting for a request to return the results, that is, the current thread is suspended, unable to engage in other tasks only when the conditions are ready to continue.
  • Non-blocking: initiating a request, the caller has been waiting for the results do not return, you can go do other things.

For life simple example, your mother let you boil water, a child you compare Bena, where the water is just wait (synchronous blocking).

And then wait for you to grow up a little bit, you know the gap can boil every time doing something else, then only need from time to time to see if the water is not opened (synchronous non-blocking).

Later, your home on the open water jug ​​will sound, so you just need to know after hearing the sound of open water, during which you can easily do their own thing, you need to pour a (non-asynchronous blocking).

 

A, Buffer (buffer)

Responsible for access to data in the Java NIO. Buffer is an array. For storing data of different data types.

/ * 
 * The different data types (except Boolean), to provide the corresponding buffer types: 
 * the ByteBuffer 
 * CharBuffer 
 * ShortBuffer 
 * IntBuffer 
 * LongBuffer 
 * FloatBuffer 
 * the DoubleBuffer 
 * 
 * buffer management described above is almost the same, obtained by the allocate () buffer 
 * /

1. Basic properties

  • Capacity (capacity): Buffer represents the maximum data capacity, buffer capacity can not be negative, and can not be changed after creation.
  • Limit (limit): a first read or write should not index data, i.e., data can not be located after the write limit. Restrict the buffer can not be negative, and can not be greater than its capacity.
  • Position (position): The next index data to be read or written. Buffer location can not be negative, and can not be greater than limit.
  • Mark (Mark) and reset (reset): is a marker index () method to specify a specific position in the Buffer Buffer by the Mark, then can be restored by calling the reset () method to this position.
  • Marker, position limits, the capacity to comply with the same formula: 0 <= mark <= position <= limit <= capacity

2. The common method

Buffer all subclasses provide two methods for data operation: get () and put () 
Gets the data in Buffer

  • get (): read a single byte
  • get (byte [] dst): a plurality of bytes read into the bulk of dst
  • get (int index): reading bytes specified index (does not move position)

Put the data into the Buffer

  • put (byte b): will order bytes into the buffer at the current location
  • put (byte [] src): src the current position in the byte write buffer
  • put (int index, byte b): Writes the specified byte index of the buffer (not moved position)

Other methods

  • Buffer clear (): empty the buffer and returns a reference to the buffer, not really delete the data in the buffer, just move to the position 0, while the limit is adjusted to capacity, marks set to -1.
  • Buffer flip (): The buffer limit set are position, and the position is set to 0, marks set to -1.

3. direct and non-direct buffer buffer

// allocate a buffer: the JVM memory 
the ByteBuffer buf = ByteBuffer.allocateDirect (1024 );
 // allocate direct buffers: local memory 
the ByteBuffer bufDirect = ByteBuffer.allocateDirect (1024 );
 // whether direct buffers 
System.out. the println (buf.isDirect ()); 

// direct byte buffer can also be directly mapped by file area FileChannel the map () method to create memory. The method returns MappedByteBuffer.

Non-direct buffers

Direct buffers

4. Simple to use

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/

Guess you like

Origin www.cnblogs.com/jhxxb/p/11272727.html