NIO流

NIO流

在这里插入图片描述

在这里插入图片描述

区别点:IO是面向流,NIO是面向缓冲区

在这里插入图片描述

https://www.cnblogs.com/androidsuperman/p/7082066.html 缓冲区buffer中的数据存取

缓冲区buffer的四个核心属性

  • position:位置,表示缓冲区中正在操作数据的位置
  • limit:界限,表示缓冲区可以操作的数据的大小(limit之后的数据都不能进行读写)
  • capacity:容量
  • mark:把position做一个标记点,如果reset方法执行后position可以回到此标记点

缓冲区buffer的两个核心方法

  • put():存入数据到缓冲区中
  • get():获取缓冲区中的数据
  • flip():在flip()方法之后才可以用get方法获取数据

代码演示:

        //获取缓存区的几个属性值
		System.out.println("===========allocate方法=========");
        ByteBuffer buf = ByteBuffer.allocate(1024);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
		//0 1024 1024
        System.out.println("============put方法==============");
        //用put方法存入数据
        String a = "qiuyiping";
        buf.put(a.getBytes());
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //9 1024 1024
        System.out.println("==================flip方法==================");
        buf.flip();
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //0 9 1024
        //用get方法获取数据
        System.out.println("==============get方法=============");
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst, 0, dst.length));
		//qiuyiping
        //用rewind方法可重复读取数据
        System.out.println("================rewind方法==================");
        buf.rewind();
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
 		//0 9 1024
       //用clear方法可以清空缓冲区,但是里面的数据依然存在
        System.out.println("================clear方法=================");
        buf.clear();
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //第一项存在,证明里面的数据存在
        System.out.println((char) buf.get());
		//0 1024 1024
        //测试mark方法和reset方法-把position恢复到mark标记处
        ByteBuffer buf = ByteBuffer.allocate(1024);
        String s = new String("qiuyping");

        buf.put(s.getBytes());
        buf.flip();
        byte[] dst = new byte[buf.limit()];
        buf.get(dst, 0, 2);
        System.out.println(new String(dst, 0, 2));
        buf.mark();
        buf.get(dst, 2, 2);
        System.out.println(new String(dst, 2, 2));
        System.out.println("此时的position值是:" + buf.position());
		//4
        buf.reset();
        System.out.println("reset之后的回到mark标记处的position值是:" + buf.position());
		//2

直接缓冲区和非直接缓冲区

在这里插入图片描述

  • allocate(1024):获取非直接缓冲区
  • allocateDirect(1024):获取直接缓冲区
  • isDirect():判断是否为直接缓冲区

代码演示:

		ByteBuffer buf = ByteBuffer.allocate(1024);
        ByteBuffer bufDirect = ByteBuffer.allocateDirect(1024);

        System.out.println(buf.isDirect());
        System.out.println(bufDirect.isDirect());

非直接缓冲区代码演示:

    @Test
    //利用通道完成文件复制(非直接缓冲区)
    public void nonDirectBuffer() throws IOException {
        long startTime = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("c://动画.jpg");
        FileOutputStream fos = new FileOutputStream("c://动画1.jpg");
        // ①获取到通道
        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();// 清空缓冲区
        }
        // 关闭连接
        outChannel.close();
        inChannel.close();
        fos.close();
        fis.close();
        long endTime = System.currentTimeMillis();
        System.out.println("非缓冲区:" + (endTime - startTime));
    }

直接缓冲区代码演示:

    @Test
    //使用直接缓冲区完成文件的复制(内存映射文件)   
    public void DirectBuffer() throws IOException {
        long startTime = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("c://动画.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("c://动画2.jpg"), StandardOpenOption.READ, StandardOpenOption.WRITE,
                StandardOpenOption.CREATE);
        // 映射文件
        MappedByteBuffer inMapperBuff = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMapperBuff = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        // 直接对缓冲区进行数据读写操作
        byte[] dst = new byte[inMapperBuff.limit()];
        inMapperBuff.get(dst);
        outMapperBuff.put(dst);
        outChannel.close();
        inChannel.close();
        long endTime = System.currentTimeMillis();
        System.out.println("内存映射文件耗时:"+(endTime-startTime));
    }

通道之间的数据传输

  • transferFrom():FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
  • transferTo():transferTo()方法将数据从FileChannel传输到其他的channel中

代码演示:

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel  fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();

long position = 0;
long count = fromChannel.size();

toChannel.transferFrom(position, count, fromChannel);
RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel  fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel  toChannel = toFile.getChannel();

long position = 0;
long count = fromChannel.size();

fromChannel.transferTo(position, count, toChannel);

分散读取和聚集写入

分散读取:把缓冲区分成多份,通道可以从多个缓冲区中读取数据

聚集写入:把多个缓冲区中的数据通过通道读取出来

    @Test
    public void bufferScatterRead() throws IOException {
        //1、获取通道
        RandomAccessFile raf = new RandomAccessFile("c://wen.txt", "rw");
        FileChannel channel = raf.getChannel();
        //2、分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(200);
        ByteBuffer buf1 = ByteBuffer.allocate(1024);
        //3、分散读取
        ByteBuffer[] bufs = {buf, buf1};
        channel.read(bufs);
        for (ByteBuffer b : bufs) {
            b.flip();
        }
        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
        
        //聚集写入
        RandomAccessFile raf1 = new RandomAccessFile("c://wen1.txt", "rw");
        FileChannel channel1 = raf1.getChannel();
        channel1.write(bufs);
    }

字符集和字符编码

字符集类Charset

编码:字符串 -> 字节数组

解码:字节数组 -> 字符串

代码演示:

    @Test
	//显示所有可获取的编码方式
    public void getAllCharset() {
        SortedMap<String, Charset> map = Charset.availableCharsets();
        for (String alias : map.keySet()) {
            System.out.println(alias);
        }
    }
    @Test
	//把字符串以某种编码方式转换成字符数组-编码
    public void transferByteAndChar(){
        //1.创建简体中文对应的charset Charset.forName(...)
        Charset charset = Charset.forName("GBK");
        //2.获取charset对象对应的编码器  charset.newEncoder()
        CharsetEncoder ce = charset.newEncoder();
        //3.创建一个CharBuffer对象
        CharBuffer cb = CharBuffer.allocate(30);
        //4.往CharBuffer对象中放入值
        cb.put("CSDN-IT专业社区");
        //5.开始读取CharBuffer值
        cb.flip();
        //6.将CharBuffer中的字符序列转换成字节序列   charset.encode(...)
        ByteBuffer bf = charset.encode(cb);
        //7.循环访问byteBuffer中的每个字节
        for (int i = 0; i < bf.limit(); i++) {
            System.out.print(bf.get(i)+" ");
        }
    }
    @Test
	//把字节数组以某种方式解码成字符串-解码
    public void transformByteBufferToCharBuffer() throws UnsupportedEncodingException, CharacterCodingException {
        //1.创建简体中文对应的charset   charset.forName(...)
        Charset charset = Charset.forName("UTF-8");
        //2.获取charset对象对应的解码 charset.newDecoder();
        CharsetDecoder cd = charset.newDecoder();
        //3.创建一个ByteBuffer对象,并存入值且准备读取
        ByteBuffer bf = ByteBuffer.allocate(30);
        bf.put("CSDN-IT专业社区".getBytes("UTF-8"));
        bf.flip();
        //4.将ByteBuffer的数据解码成字符序列   (...).decode(...)
        System.out.println(cd.decode(bf));
    }

NIO网络通信的阻塞与非阻塞式

阻塞式

在这里插入图片描述

阻塞式是服务器不能判断客户端什么时候发送数据过来,所以在不确定的过程中一直等待,此时服务器不能做什么事,因此处于阻塞状态,消耗了CPU的性能。传统IO就是这个过程,解决方法是在服务器端开启多个线程处理多个客户端的请求,即使有线程被阻塞了,其它线程还是可以运行,但CPU效率依然有损耗

非阻塞式

在这里插入图片描述

非阻塞式采用了selector选择器做中间人,其中注册了多个Channel,而channel中存放了buffer,只要Channel中的数据准备就绪了,才会把该channel和服务器端的某一个线程做对接,这样服务器不会处于等待过程,就不会有阻塞

使用 NIO 完成网络通信的三个核心

  1. 通道(Channel):负责连接

java.nio.channels.Channel 接口:

​ |–SelectableChannel

​ |–SocketChannel

​ |–ServerSocketChannel

​ |–DatagramChannel

​ |–Pipe.SinkChannel

​ |–Pipe.SourceChannel

  1. 缓冲区(Buffer):负责数据的存取
  1. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况

代码演示:

https://blog.csdn.net/xiaosong_2016/article/details/78767931(阻塞式和非阻塞式)

管道方式传输数据

代码演示:

    @Test
    public void testPipeChannel() throws IOException {
        //1.获取管道
        Pipe pipe = Pipe.open();
        //2.通过管道获取sinkChannel,将缓冲区中的数据写入sinkChannel
        Pipe.SinkChannel sinkChannel = pipe.sink();
        ByteBuffer buf = ByteBuffer.allocate(1024);
        buf.put("数据通过pipe通道传输".getBytes());
        buf.flip();
        sinkChannOel.write(buf);
        //3.通过管道获取sourceChannel,通过sourceChannel读取缓冲区中的数据
        Pipe.SourceChannel sourceChannel = pipe.source();
        buf.flip();
        int len = sourceChannel.read(buf);
        System.out.println(new String(buf.array(), 0, len));
        //4.关闭所有通道
        sourceChannel.close();
        sinkChannel.close();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值