NIO学习笔记

一、缓冲区

/**
 * 缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
 *
 * 缓冲区的存储数据的两个核心方法:
 * 1. put():存入数据到缓冲区中
 * 2. get():获取缓冲区中的数据
 *
 * 缓冲区中的四个核心属性:
 * 1. capacity:容量,表示缓冲区中最大数据存储量,一旦声明不能改变
 * 2. limit:界限,表示缓冲区中可以操作数据的大小(limit后数据不能进行读写)
 * 3. position:位置,表示缓冲区中正在操作数据的位置
 * 4. mark:标记,表示记录当前position位置,可通过reset()恢复到mark位置
 *
 * 0 <= mark <= position <= limit <= capacity
 */
public class TestBuffer {
    public static void main(String[] args) {
        // 1. 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        System.out.println(buf.capacity()); //1024
        System.out.println(buf.limit()); // 1024
        System.out.println(buf.position()); // 0

        // 2. 利用put方法存入数据到缓冲区
        String str = "abcde";
        buf.put(str.getBytes(StandardCharsets.UTF_8));
        System.out.println(buf.capacity()); //1024
        System.out.println(buf.limit()); // 1024
        System.out.println(buf.position()); // 5

        // 3. 切换到读数据模式
        buf.flip();
        System.out.println(buf.capacity()); //1024
        System.out.println(buf.limit()); // 5
        System.out.println(buf.position()); // 0

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

        System.out.println(buf.capacity()); //1024
        System.out.println(buf.limit()); // 5
        System.out.println(buf.position()); // 5

        //5. rewind() 可重复读数据 重新开始读
        //6. clear() 清空缓冲区 回到最初状态 但是缓冲区中的数据依然存在,只是处于"被遗忘状态"
        buf.clear();
        System.out.println((char) buf.get());  // a
    }
}

1.1 缓冲区的概念

在这里插入图片描述

1.2 缓冲区的基本属性

在这里插入图片描述
在这里插入图片描述

1.3 Buffer常用方法

在这里插入图片描述

1.4 缓冲区中对数据的操作

在这里插入图片描述

二、直接与非直接缓冲区

2.1 概念

在这里插入图片描述

2.2 区别

在这里插入图片描述
在这里插入图片描述

2.3 源码

// 直接缓冲区与非直接缓冲区
    // 1. 非直接缓冲区:通过allocate方法分配缓冲区,将缓冲区建立在JVM内存当中
    // 2. 直接缓冲区:通过allocateDirect方法分配直接缓冲区,将缓冲区建立在物理内存中,提高效率
    public void test1() {
        // 分配非直接缓冲区
        ByteBuffer bb1 = ByteBuffer.allocate(1024);
        // 分配直接缓冲区
        ByteBuffer bb2 = ByteBuffer.allocateDirect(1024);
    }
2.3.1 非直接缓冲区源码
	public static ByteBuffer allocate(int capacity) {
	        if (capacity < 0)
	            throw new IllegalArgumentException();
	        return new HeapByteBuffer(capacity, capacity);
	}
	
	===================================================================
	
	HeapByteBuffer(int cap, int lim) {  // 对字节数组的操作,不涉及操作系统
        super(-1, 0, lim, cap, new byte[cap], 0);
    }
2.3.2 直接缓冲区源码
	public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }
	
	===========================================================
	
	DirectByteBuffer(int cap) {  

        super(-1, 0, cap, cap);
        // 下面的这个方法就是直接在操作系统内存中操作的方法
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

三、通道

3.1 概念

在这里插入图片描述
在这里插入图片描述

	/**
	 * 1. 通道(Channel):用于源节点与目标节点的连接。
	 * 在java NIO 中负责缓冲区数据的传输。Channel本身不存储数据,所以需要配合缓冲区进行传输
	 * 类似于(有铁路没有用,需要有火车来存储,才能运输)
	 *
	 * 2. 通道的主要实现类:
	 *      java.nio.channels.Channel 接口:
	 *          |-- FileChannel(用于本地文件的传输)
	 *          |-- SocketChannel (以下三个都是用于网络文件的传输)
	 *          |-- ServerSocketChannel
	 *          |-- DatagramChannel
	 *
	 * 3. 获取通道
	 *  1). JAVA 针对支持通道的类提供了getChannel()方法:
	 *      本地IO:
	 *          FileInputStream/FileOutputStream
	 *          RandomAccessFile
	 *      网络IO:
	 *          Socket  ServerSocket  DatagramSocket
	 *  2).在 JDK1.7 中的NIO2 针对各个通道提供了静态方法open()
	 *  3).在 JDK1.7 中的NIO2 的Files 工具类中的newByteChannel()
	 */

3.2 非直接缓冲区的通道(基于本地文件传输)

	// 利用通道完成文件的复制(非直接缓冲区)
    public void test() {
        FileInputStream fis = null;
        FileOutputStream fos = null;

        FileChannel inChannel = null;
        FileChannel outChannel = null;

        try {
            fis = new FileInputStream("1.jpg");
            fos = new FileOutputStream("2.jpg");

            // 获取通道
            inChannel = fis.getChannel();
            outChannel = fos.getChannel();

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

            // 将通道中的数据存入到缓冲区中
            while (inChannel.read(buffer) != -1) {  // 还有数据
                // 切换到读数据模式
                buffer.flip();
                // 将缓冲区中的数写入到通道中
                outChannel.write(buffer);
                // 清空缓冲区 以便于下次读取
                buffer.clear();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (outChannel != null) {
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

3.3 直接缓冲区的通道(基于本地文件传输)

	// 使用通道完成文件的复制(直接缓冲区--内存映射文件)
    public void test2() throws Exception{
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE);

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

        // 直接对缓存区数据进行读写操作
        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);

        inChannel.close();
        outChannel.close();
    }
	// 通道之间直接的数据传输(直接缓冲区) transferFrom()  transferTo()
    public void test3() throws Exception{
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE);

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

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

四、分散和聚集

4.1 概念

在这里插入图片描述
在这里插入图片描述

4.2 代码

	/**
     * 分散读取(Scattering Reads): 将通道中的数据分散到多个缓冲区中
     * 聚集写入(Gathering Writes): 将多个缓冲区中的数据聚集到通道中
     */
    public void test4() throws Exception{
        RandomAccessFile raf1 = new RandomAccessFile("a.txt", "rw");

        // 1 获取通道
        FileChannel channel1 = raf1.getChannel();

        // 2 分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(256);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        // 3 分散读取
        ByteBuffer[] buffers = new ByteBuffer[]{buf1, buf2};
        channel1.read(buffers);

        // 4 查看缓冲区中的数据
        for (ByteBuffer buffer : buffers) {
            buffer.flip();
        }

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

        // 5 聚集写入
        RandomAccessFile raf2 = new RandomAccessFile("b.txt", "rw");
        FileChannel channel2 = raf2.getChannel();
        channel2.write(buffers);
    }
	
  ===================================运行结果=======================================
linux -- SVN -- Git -- Dubbo -- SpringSession -- Redis -- RabbitMQ -- Mycat -- Nginx -- FastDFS -- Springboot -- Spring Cloud Netflix -- Spirng Cloud Alibaba -- Docker -- K8S

 Maven -- Linux -- Redis6 -- Git -- Docker -- Elasticsearch -- Dubbo -- Nginx 
++++++++++++++++++++++++++++++
-- ActiceMQ -- Spring Security -- JVM -- JUC -- MySql高级 -- Mycat -- k8s -- Shiro

Maven -- Linux --  Git -- Redis -- Dubbo -- SpringSession -- RabbitMQ -- Nginx -- ActiveMQ -- Shiro -- Spring Security -- Docker -- Elasticsearch -- k8s --  Mysql高级 -- Mycat

4.3 FileChannel常用方法

在这里插入图片描述

五、字符集

	/**
     * 字符集:Charset
     * 编码: 字符串 -> 字符数组
     * 解码: 字符数组 -> 字符串
     */
    public void test5() throws Exception{
        /*
        // 获取所有字符集
        SortedMap<String, Charset> map = Charset.availableCharsets();
        Set<Map.Entry<String, Charset>> set = map.entrySet();

        for (Map.Entry<String, Charset> stringCharsetEntry : set) {
            System.out.println(stringCharsetEntry.getKey() + "=" + stringCharsetEntry.getValue());
        }*/

        Charset cs1 = Charset.forName("GBK");
        // 获取编码器
        CharsetEncoder ce = cs1.newEncoder();
        // 获取解码器
        CharsetDecoder cd = cs1.newDecoder();

        CharBuffer cBuf = CharBuffer.allocate(1024);
        cBuf.put("中北大学!");
        cBuf.flip();

        // 编码
        ByteBuffer bBuf = ce.encode(cBuf);
        for (int i = 0; i < bBuf.limit(); i++) {
            System.out.println(bBuf.get());
        }

        // 解码
        bBuf.flip();
        CharBuffer cBuf2 = cd.decode(bBuf);
        System.out.println(cBuf2.toString());
    }

六、NIO 非阻塞式网络通信

6.1 基础知识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/**
 * 使用NIO完成网络通信的三个核心:
 * 1. 通道(Channel):负责连接
 *      java.nio.channels.Channel接口:
 *          |-> SelectableChannel
 *              |->SocketChannel
 *              |->ServerSocketChannel
 *              |->DatagramChannel
 *
 *              |->Pipe.SinkChannel
 *              |->Pipe.SourceChannel
 *
 * 2. 缓冲区(Buffer):负责数据的存取
 * 3. 选择器(Selector):是SelectableChannel的多路服用器。用于监控SelectableChannel的IO状况
 */

6.2 阻塞式

	// 客户端
    @Test
    public void client() throws Exception{
        // 获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);

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

        // 读取本地文件,并发送到服务端
        while (inChannel.read(buf) != -1) {
            buf.flip();
            sChannel.write(buf);
            buf.clear();
        }

        // 关闭通道
        inChannel.close();
        sChannel.close();
    }

    // 服务端
    @Test
    public void server() throws Exception {
        // 获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.READ);

        // 绑定连接
        ssChannel.bind(new InetSocketAddress(9898));

        // 获取客户端连接的通道
        SocketChannel sChannel = ssChannel.accept();

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

        // 接收客户端数据,并保存到本地
        while (sChannel.read(buf) != -1) {
            buf.flip();
            outChannel.read(buf);
            buf.clear();
        }

        // 关闭通道
        sChannel.close();
        outChannel.close();
        ssChannel.close();
    }

6.3 非阻塞式

在这里插入图片描述
在这里插入图片描述

6.3.1 (SocketChannel & ServerSocketChannel)

@Test
    public void client() throws Exception {
        // 获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        // 切换为非阻塞模式
        sChannel.configureBlocking(false);

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

        // 发送数据给客户端
        buf.put(new Date().toString().getBytes());
        buf.flip();
        sChannel.write(buf);
        buf.clear();

        // 关闭通道
        sChannel.close();
    }

    @Test
    public void server() throws Exception {
        // 获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();

        // 切换为非阻塞模式
        ssChannel.configureBlocking(false);

        // 绑定连接
        ssChannel.bind(new InetSocketAddress(9898));

        // 获取选择器
        Selector selector = Selector.open();

        // 将通道注册到选择器上,并指定 监听'接收'事件
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 轮询式的获取选择器上已经 准备就绪 的事件
        while (selector.select() > 0) {
            // 获取当前选择器中所有已经注册的 选择键(已就绪的监听事件)
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();

            while (it.hasNext()) {
                // 获取 准备就绪 的事件
                SelectionKey selectionKey = it.next();
                // 判断具体是什么事件准备就绪
                if (selectionKey.isAcceptable()) {
                    // 若 接收就绪 获取客户端连接
                    SocketChannel sChannel = ssChannel.accept();
                    // 切换非阻塞模式
                    sChannel.configureBlocking(false);
                    // 将该通道注册到选择器上
                    sChannel.register(selector, SelectionKey.OP_READ);
                }else if (selectionKey.isReadable()) {
                    // 获取当前选择器上 读就绪 状态的通道
                    SocketChannel sChannel = (SocketChannel) selectionKey.channel();

                    // 读取数据
                    ByteBuffer buf = ByteBuffer.allocate(1024);

                    int len = 0;
                    while ((len = sChannel.read(buf)) > 0) {
                        buf.flip();
                        System.out.println(new String(buf.array(), 0, len));
                        buf.clear();
                    }
                }

                // 取消选择键
                it.remove();
            }
        }

        ssChannel.close();
    }

6.2.2 (DatagramChannel)

在这里插入图片描述

	@Test
    public void client() throws Exception{
        DatagramChannel dc = DatagramChannel.open();

        dc.configureBlocking(false);

        ByteBuffer buf = ByteBuffer.allocate(1024);
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            String str = sc.next();
            buf.put((LocalDateTime.now().toString() + "->" + str).getBytes());
            buf.flip();
            dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
            buf.clear();
        }
        dc.close();
    }

    @Test
    public void server() throws Exception{
        DatagramChannel dc = DatagramChannel.open();

        dc.configureBlocking(false);

        dc.bind(new InetSocketAddress(9898));
        Selector selector = Selector.open();
        dc.register(selector, SelectionKey.OP_READ);
        while (selector.select() > 0) {
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                SelectionKey sk = it.next();

                if (sk.isReadable()) {
                    ByteBuffer buf = ByteBuffer.allocate(1024);

                    dc.receive(buf);
                    buf.flip();
                    System.out.println(new String(buf.array(), 0, buf.limit()));
                    buf.clear();
                }
                it.remove();
            }
        }
    }

七、管道

在这里插入图片描述

7.1 使用

在这里插入图片描述
在这里插入图片描述

八、补充知识(NIO2 / Path / Paths / Files / try语法糖)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值