Java粗浅认识-I/O(二)

NIO简介

什么是Java NIO,nio在java1.4时新增,叫做new I/O,就是新的I/O,既是在基于1.0出现的I/O Stream操作之上的新改变,
包括,新的 I/O通信模型,如Buffer,Channels,多路复用(Selector);基于Perl样式正则表达式的模式匹配工具。

java.nio.Buffer

一个特点原始数据类型(并不包括如String等类)的集合,提供了方便的put,get,数据批量移动等操作,就像我们用数组来缓存每次读取的数据一样,但提供了更加方便的操作,还有一个特殊的基于直接内存来分配的ByteBuffer.allocateDirect()的操作,基于直接内存来操作,虽然在gc中并不回收这部分内存,但大数据的操作效率更高。

Buffer内部维护0<=mark<=position<=limit<=capacity的指针,来操作缓存数据,对外提供各种类型的put,get,批量移动;对缓存容器进行创建,翻转,释放,压缩,标记
缓冲区的使用实例

public static void main(String[] args) {
        //1.基于allocate方法调用得到的heap(JVM管理的堆)上的缓存
        ByteBuffer byteBuffer = ByteBuffer.allocate(8);
        byteBuffer.put((byte) 'a');
        byteBuffer.put((byte) 'b');
        byteBuffer.put((byte) 'c');
        //翻转函数,设置limit = position,position = 0,
        byteBuffer.flip();
        //hasRemaining = limit - position>0
        while (byteBuffer.hasRemaining()) {
            //position = position+1*(1byte)
            System.out.println((char) byteBuffer.get());
        }
        //直接返回当前这个Buffer底层的数组,和position,limit等下标操纵无关
        byte[] bytes = byteBuffer.array();
        for (byte aByte : bytes) {
            System.out.println(aByte);
        }

        //int类型的Buffer,同理其他的Buffer也是如此
        IntBuffer intBuffer = IntBuffer.allocate(8);
        intBuffer.put(1);
        intBuffer.put(2);
        intBuffer.put(3);

        intBuffer.flip();
        while (intBuffer.hasRemaining()) {
            System.out.println(intBuffer.get());
        }

        //2.基于Wrap()方法得到heap(JVM管理的堆)上的缓存
        //基础类型的Wrap(),直接创建一个数组长度的缓冲区,position=0,limit =capacity =  length;
        LongBuffer longBuffer = LongBuffer.wrap(new long[]{1L, 2L, 3L});
        //因为offset=position =0,所以不用再次flip
        while (longBuffer.hasRemaining()) {
            System.out.println("long:" + longBuffer.get());
        }
        //已经到结尾了,所以要重置一下指针,内部的数据并未发生改变
        //position = 0;
        //limit = capacity;
        //mark = -1;
        longBuffer.clear();
        longBuffer.put(4L);
        longBuffer.flip();
        while (longBuffer.hasRemaining()) {
            System.out.println("long:" + longBuffer.get());
        }

        // 这是为了方便处理字符串处理而实现的特例,包装CharSequence,如String,StringBuffer,StringBuilder
        CharSequence charBuffer = CharBuffer.wrap("hello world");
        while (((CharBuffer) charBuffer).hasRemaining()) {
            System.out.println("char:" + ((CharBuffer) charBuffer).get());
        }

        //批量移动,把float[]的数批量put到floatBuffer中,然后再get出floatBuffer的数据,放在tmpBuffer中
        FloatBuffer floatBuffer = FloatBuffer.allocate(8);
        floatBuffer.put(new float[]{0.4f, 0.2f, 0.3f});
        float[] tmpBuffer = new float[4];
        floatBuffer.flip();
        floatBuffer.get(tmpBuffer, 0, floatBuffer.remaining());
        for (float v : tmpBuffer) {
            System.out.println("float:" + v);
        }

        DoubleBuffer doubleBuffer = DoubleBuffer.allocate(8);
        doubleBuffer.put(new double[]{0.5d, 0.6d, 0.7d});

        //mark一下position,mark = position
        doubleBuffer.mark();
        doubleBuffer.put(new double[]{0.8d, 0.9d, 0.11d});
        //position =  mark;
        doubleBuffer.reset();
        //position = mark,limit = capacity,所以打印出来了最后两个没有实际赋值的0.0
        while (doubleBuffer.hasRemaining()) {
            System.out.println("double:" + doubleBuffer.get());
        }

        ShortBuffer shortBuffer = ShortBuffer.allocate(8);
        shortBuffer.put(new short[]{1, 2, 3, 4, 5, 6});
        System.out.println("shortBuffer.position:" + shortBuffer.position());
        //手动设置position
        shortBuffer.position(3);
        //剩余position个数据,limit=capacity
        shortBuffer.compact();
        while (shortBuffer.hasRemaining()) {
            System.out.println("short:" + shortBuffer.get());
        }

        //直接内存缓冲区,1char = 2byte,1short=2byte,1int = 4byte,1long = 8byte,1float = 4byte,1double = 8byte
        //不会被gc的区域
        ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(8);
        directByteBuffer.putInt(10).putInt(11);
        directByteBuffer.flip();
        //1.直接类型映射
        /*while (directByteBuffer.hasRemaining()) {
            System.out.println("directByteBuffer:" + directByteBuffer.getInt());
        }*/
        //2.buffer类型转换
        IntBuffer intBuffer1 = directByteBuffer.asIntBuffer();
        while(intBuffer1.hasRemaining()){
            System.out.println("directByteBuffer:" + intBuffer1.get());
        }
    }

java.nio.channels.Channel

什么是channel,channel是(A nexus for I/O operations.),一个i/o操作的连接。

FileChannel文件拷贝实例

/**
     * 利用transfer直接拷贝
     */
    private static void copyFile2(String source, String target) throws IOException {
        File fileRead = new File(source);
        File fileWrite = new File(target);

        RandomAccessFile randomAccessFileRead = new RandomAccessFile(fileRead, "r");
        RandomAccessFile randomAccessFileWrite = new RandomAccessFile(fileWrite, "rw");
        FileChannel fileChannelRead = randomAccessFileRead.getChannel();
        FileChannel fileChannelWrite = randomAccessFileWrite.getChannel();
        fileChannelRead.transferTo(0, fileChannelRead.size(), fileChannelWrite);
        fileChannelRead.close();
        fileChannelWrite.close();
    }

    /**
     * 手工拷贝
     * @param source
     * @param target
     * @throws IOException
     */
    private static void copyFile1(String source, String target) throws IOException {

        File fileRead = new File(source);
        File fileWrite = new File(target);

        RandomAccessFile randomAccessFileRead = new RandomAccessFile(fileRead, "r");
        RandomAccessFile randomAccessFileWrite = new RandomAccessFile(fileWrite, "rw");
        //16k
        ByteBuffer buffer = ByteBuffer.allocateDirect(1 << 14);
        FileChannel fileChannelRead = randomAccessFileRead.getChannel();
        FileChannel fileChannelWrite = randomAccessFileWrite.getChannel();
        while (fileChannelRead.read(buffer) != 0) {
            buffer.flip();
            fileChannelWrite.write(buffer);
        }
        fileChannelRead.close();
        fileChannelWrite.close();
    }

不带Selector的NIO网络通信实例

server,服务端监听端口8888,等待服务端连接,连接完成后,直接在当前线程(main线程)中,接受来自客户端的文件,并写出到文件中。

private static void server(String filename) throws IOException, InterruptedException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            if (socketChannel == null) {
                System.out.println("等待客户端连接.");
                TimeUnit.SECONDS.sleep(2);
                continue;
            }
            //16k
            ByteBuffer buffer = ByteBuffer.allocate(1 << 14);
            RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "rw");
            while (socketChannel.read(buffer) != 0) {
                buffer.flip();
                randomAccessFile.getChannel().write(buffer);
            }
            if (socketChannel.isOpen()) {
                socketChannel.close();
            }
            break;
        }
        serverSocketChannel.close();
    }

client

客户端连接127.0.0.1端口为8888的服务,然后发送数据,等待服务端接受。

    private static void client(String filename) throws IOException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8888));
        RandomAccessFile randomAccessFile = new RandomAccessFile(filename, "r");
        //16k
        ByteBuffer buffer = ByteBuffer.allocateDirect(1 << 14);
        while (randomAccessFile.getChannel().read(buffer) != 0) {
            buffer.flip();
            socketChannel.write(buffer);
        }
        randomAccessFile.close();
        socketChannel.close();
    }

java.nio.channels.Selector(对象的多路复用器)

多个channel可以同时注册到同一个Selector中,Selector通过唯一的一个SelectionKey与一个通道建立联系,而每个SelectionKey都设置了一个关注事件类型,包括, OP_ACCEPTOP_READOP_WRITEOP_CONNECT四种事件类型。

使用Selector选择器的服务端实例(客户端可以使用上面的客户端)

 private static void server(String filename) throws IOException, InterruptedException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            //阻塞等待连接
            int complement = selector.select();
            if (complement == 0) {
                continue;
            }
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel();
                    System.out.println("接受連接。");
                    SocketChannel socketChannel = serverSocketChannel1.accept();
                    if(socketChannel==null){
                        continue;
                    }
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    //16k
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1 << 14);
                    FileChannel fileChannel = new RandomAccessFile(filename, "rw").getChannel();
                    while (socketChannel.read(byteBuffer) > 0) {
                        byteBuffer.flip();
                        fileChannel.write(byteBuffer);
                        byteBuffer.clear();
                    }
                    fileChannel.close();
                    socketChannel.close();
                }
                iterator.remove();
            }
        }
    }

这一篇写了对Nio的理解,主要是Buffer、Channel、Selector的类,下一篇讲解在Java7中Nio2。

猜你喜欢

转载自blog.csdn.net/bpz31456/article/details/85049252