NIO笔记

NIO笔记

一.IO是面向流,单向的;NIO是面向缓冲区

面向流是指在建立通道后,在通道上读取数据,直至读取到所有数据,数据没有被缓存到其他地方 ;

单向是指读数据要输入流,写数据要输出流;

缓存区先把数据存储在缓冲区,再通过通道把缓冲区的数据传输出去,而缓冲区既可以输入又可以输出;

1.1缓冲区(buffer)

在JAVA NIO中负责数据的存取;缓冲区相当于数组,不同类型有不同的缓冲区

(ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,DoubleBuffer)

缓冲区的四大属性:

capacity:容量,表示缓冲区的最大容量

limit:界限,表示缓冲区中可以操作数据的大小(limit后面的数据不能进行读写)

position:位置,表示缓冲区中正在操作数据的位置

mark:标记,表示记录当前position的位置,可以通过调用reset()把position恢复到mark()

        //获得一个指定大小的非直接缓冲区
        ByteBuffer buf  = ByteBuffer.allocate(1024);
        String str = "abcdegf";
        System.out.println(buf.capacity());//1024
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//1024
        //向缓冲区写入数据
        buf.put(str.getBytes());
        System.out.println(buf.capacity());//1024
        System.out.println(buf.position());//7
        System.out.println(buf.limit());//1024
        //切换从缓存区读取数据模式
        buf.flip();
		System.out.println(buf.capacity());//1024
        System.out.println(buf.position());//0
        System.out.println(buf.limit());//7
        //从缓存区读取数据
        byte [] bys = new byte[buf.limit()-1];
        buf.get(bys);
        System.out.println(new String(bys,0,bys.length));
        //可重复读取缓冲区的数据
        buf.rewind();
        //清除缓冲区,数据依然存在,但处于被遗忘的状态,又可以重写写数据
        buf.clear();
        if(buf.hasRemaining()){//判断是否还有数据没有读完,以position是否到达limit为标准
            System.out.println("---"+buf.remaining());//返回limit()-position()
        }
		//mark存储当前position的位置
        buf.mark();
        //把position恢复到mark的标记的位置
        buf.reset();

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

非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM内存中

直接缓存区:通过allocateDirect()方法,将缓存区建立在OS物理内存中,少了OS缓冲区与jvm缓冲区之间的传输,提高了效率,但要等垃圾回收机制来释放缓冲区与程序的连接

        //获取直接缓冲区
       ByteBuffer buf_2 = ByteBuffer.allocateDirect(1024);
1.2通道(Channel)

DMA:通过向CPU发出请求来获取管理IO流的权力

通道(channel):全独立的处理器用于处理IO操作

通道通过非直接缓冲区传输文件

        FileInputStream fis = new FileInputStream("文件1.txt");
        FileOutputStream fos = new FileOutputStream("文件2.txt");

        //获取通道
        FileChannel inChannle = fis.getChannel();
        FileChannel onChannle = fos.getChannel();

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

        while(inChannle.read(buf)!=-1){//缓冲区读取文件
            buf.flip();
            onChannle.write(buf);//缓冲区写入文件
            buf.clear();//清空缓冲区,重新读取文件
        }

通过直接缓冲区传输文件的两种方式

内存映射文件

        FileChannel inChannel = FileChannel.open(Paths.get("文件1.txt"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("文件2.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

        //内存映射文件
        //把文件存放到缓冲区
        MappedByteBuffer inMapperBuf = inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        //读取缓冲区的文件
        MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());

        byte[] bt = new byte[inMapperBuf.limit()];
        inMapperBuf.get(bt);
        outMappedBuf.put(bt);

通道之间的数据传输

//inChannel.transferTo(0,inChannel.size(),onChannel);两者一样
outChannel.transferFrom(inChannel,0,inChannel.size());

分散与聚集

分散读取:将通道中的数据分散到多个缓冲区中

聚集写入:将多个缓冲区的数据聚集到通道中


二,IO是阻塞式,NIO是非阻塞式的

阻塞状态:一个线程调用read()或者write()时,线程会被堵塞,知道数据被读取完或写入完,前面的完成不了后面的也无法完成(用多线程可解决);

非阻塞状态:通过把通道注册到选择器上,选择器判断通道是否准备就绪,准备就绪后才会发到服务端的线程上,在被运行;客户端未准备就绪,服务端可以运行其他准备就绪任务;

非阻塞状态的Socket连接

public static void server() throws IOException {
        //1.获取通道
        ServerSocketChannel ssChannel = ServerSocketChannel.open();
        //2.切换非堵塞模式
        ssChannel.configureBlocking(false);
        //3.绑定连接
        ssChannel.bind(new InetSocketAddress(8888));
        //4.获取选择器
        Selector selector = Selector.open();
        //5.将选择器注册到通道上,并且指定监听接收事件事件
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
        //6.轮询式的获取选择器上已经“准备就绪”的事件
        while(selector.select()>0){//大于0说明有准备就绪的事件
            //7.获取当前选择器中所有注册的“选择键(已就绪的监听状态)”
            Iterator<SelectionKey> it = (Iterator<SelectionKey>) selector.selectedKeys().iterator();
            while(it.hasNext()) {
                //8.获取准备就绪的事件
                SelectionKey sk = it.next();
                //9.判断具体是什么事件准备就绪
                if(sk.isAcceptable()){
                    //10.若“接收就绪”,获取客户端的连接
                    SocketChannel sChannel = ssChannel.accept();
                    //11.切换非阻塞模式
                    sChannel.configureBlocking(false);
                    //12.将该通道注册到选择器上
                    sChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
                    //13.获取当前选择器上“读就绪”状态的通道
                    SocketChannel sChannel = (SocketChannel)sk.channel();
                    //14.读取数据
                    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();
            }
        }

    }
 public static void client() throws IOException {
        //1.获取通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));

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

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

        //4.发送数据给服务器
        buf.put(new Date().toString().getBytes());
        buf.flip();
        sChannel.write(buf);
        buf.clear();

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

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值