为什么NIO性能好?

简介

核心API:

  • Channels:channel像一种管道,数据可以从channel到Buffer,其主要实现FileChannel(文件读取数据)、DatagramChannel(UDP读取数据)、SocketChannel(TCP读取数据)、ServiceSocketChannel(监听TCP连接)

  • Buffers:缓冲区,一次性把数据读到缓冲区,通过Channel进行数据处理,其主要实现ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

  • Selectors:处理器,可以运行单个线程处理多个Channel

工具类:

  • Pipe:两个线程之间的单向数据连接,有一个sources通道和sink通道、数据会被写到sink通道、从source通道读取

  • FileLock:只有一个进程可以拿到文件锁

详解

Channel

特性

  • 即可从通道中读取数据、也可以写数据到通道

  • 通道可以异步读写

  • 通道中的数据总要先读到一个buffer、或者总要写入一个buffer

实现

FileChannel

无法设置为非阻塞模式,它总是运行在阻塞模式下

读数据:

RandomAccessFile:打开文件

file.getChannel():获取FileChannel实例

ByteBuffer.allocate(1024):设置一个缓冲区

inChannel.read():从fileChannel中读取数据到Buffer,如果是-1,表示文件到了末尾

buffer.flip():将Buffer从写模式切换到读模式

buffer.hasRemaining():是否达到缓冲区上界

buffer.compact():清空缓冲区

inChannel.close():关闭通道

    public static void main(String[] args) {
        RandomAccessFile file;
        try {
            file = new RandomAccessFile("D:\\aaa.txt", "rw");
            FileChannel inChannel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while(true) {
                int count = inChannel.read(buffer);
                if (count <= -1) {
                    break;
                }
                buffer.flip();
                while (buffer.hasRemaining()) {
                    char ch = (char) buffer.get();
                    System.out.print(ch);
                }
                buffer.compact();
                inChannel.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

写数据:

buffer.put:设置数据

inChannel.write(buffer):向通道中写数据,需要循环调用,因为无法保证write()方法一次向FileChannel写入多少个字节

public static void main(String[] args) {
        RandomAccessFile file;
        try {
            String data = "hello fileChannel";
            file = new RandomAccessFile("D:\\aaa.txt", "rw");
            FileChannel inChannel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.clear();
            buffer.put(data.getBytes());
            buffer.flip();
            while(buffer.hasRemaining()) {
                inChannel.write(buffer);
            }
            inChannel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

DatagramChannel

DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。

接收数据

DatagramChannel.open():打开通道

channel.receive():接收数据

public static void main(String[] args) {
        DatagramChannel channel;
        try {
            channel = DatagramChannel.open();
            channel.socket().bind(new InetSocketAddress(9999));
            ByteBuffer buffer = ByteBuffer.allocate(48);
            buffer.clear();
            channel.receive(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

发送数据

channel.send():发送数据

public static void main(String[] args) {
        DatagramChannel channel;
        try {
            channel = DatagramChannel.open();
            channel.socket().bind(new InetSocketAddress(9999));
            String data = "hello DatagramChannel ";
            ByteBuffer buf = ByteBuffer.allocate(48);
            buf.clear();
            buf.put(data.getBytes());
            buf.flip();
            int bytesSent = channel.send(buf, new InetSocketAddress("ccc.com", 80));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

SocketChannel

读数据

SocketChannel.open():打开socketChannel通道

socketChannel.connect:连接socket地址

public static void main(String[] args) {
        SocketChannel socketChannel;
        try {
            socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("http://cc.com", 80));
            ByteBuffer buffer = ByteBuffer.allocate(48);
            int bytesRead = socketChannel.read(buffer);
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

写数据

public static void main(String[] args) {
        SocketChannel socketChannel;
        RandomAccessFile file;
        try {
            String data = "hello fileChannel";
            file = new RandomAccessFile("D:\\aaa.txt", "rw");
            socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("http://cc.com", 80));
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.clear();
            buffer.put(data.getBytes());
            buffer.flip();
            while(buffer.hasRemaining()) {
                socketChannel.write(buffer);
            }
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Selector

创建

调用open创建一个Selector

Selectorselector=Selector.open();

注册通道

Channel必须处于非阻塞模式下,FileChannel是阻塞模式不能与Selector一起使用

register:第二个参数可以监听四种不同的类型Connect、Accept、Read、Write

channel.configureBlocking(false);
SelectionKey key = channel.register(selector,Selectionkey.OP_READ);

示例

selector.select():阻塞到至少有一个通道在你注册的事件上就绪了

selectedKeys():访问注册的channel

public static void main(String[] args) {
        SocketChannel channel;
        try {
            channel = SocketChannel.open();
            channel.connect(new InetSocketAddress("http://cc.com", 80));
            Selector selector = Selector.open();
            channel.configureBlocking(false);
            SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
            while(true) {
                int readyChannels = selector.select();
                if(readyChannels == 0) continue;
                Set selectedKeys = selector.selectedKeys();
                Iterator keyIterator = selectedKeys.iterator();
                while(keyIterator.hasNext()) {  
                    SelectionKey key1 = (SelectionKey) keyIterator.next();
                            if(key1.isAcceptable()) {
                            } else if (key1.isConnectable()) {
                            } else if (key1.isReadable()) {
                            } else if (key1.isWritable()) {
                            }
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

Buffer

基本用法

  • 写入数据到buffer

  • 调用flip()方法,写模式切换到读模式

  • 从buffer中读取数据

  • 调用clear或者compact清楚数据

capacity,position和limit

  • capacity:内存块,固定大小,满了之后需要清楚数据之后才能写数据

  • position:当前位置,初始值为0,当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1。

  • limit:写模式下可以控制buffer写多少数据,读模式下控制读多少数据

发布了75 篇原创文章 · 获赞 85 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/zhangchangbin123/article/details/93149007