高并发编程基础——初识NIO

引入:

基础概念:

  • 同步:一个对象或者逻辑,在一个时间段内只允许一个线程操作,就是同步操作。
  • 异步:一个对象或者逻辑,在一个时间段内只允许多个线程操作,就是异步操作。
  • 阻塞:线程没有获取到想要的结果,就会停留在原地等待。
  • 非阻塞:线程无论有没有获取到想要的结果,都会继续往下走。

Java基础中,学过IO的操作,我们把它叫做BIO,也就是同步阻塞式IO,同步阻塞式IO有它不好的地方,比如它的效率很低,保持恶意连接线程无法释放。所以我们今天学习的就是NIO(同步非阻塞式线程),他的作用就是传输数据

NIO的三大组件:

NIO有三大组件,分别是buffer,channel,selector。

buffer:

buffer顾名思义叫缓冲区,作用就是用来存储数据的。

可存储类型:
buffer中可以存储很多的基本数据类型,从这些名字可以很容易看出来,最常用的是ByteBuffer,所以下面就用ByteBuffer来做案例。
高并发编程基础——初识NIO_第1张图片

buffer中的一些重要位置:
capacity:用来指定缓存区的容量大小,一但声明了就不能改变
position:指向需要写入的位置,初始化的时候position=0,如果后面写入数据到缓存,position自动往后移动。
limit:position所能到达的最大位置,可以在遍历中使用,把position的位置给limit后,position=0,然后遍历,这样就可以避免遍历出那些没有存储东西的单元。buffer初始化的时候,position和limit重合。
高并发编程基础——初识NIO_第2张图片

channel:

在java网络编程中我们学习过TCP和UDP,那么就对channel一定不会陌生,他的作用就是用来传输数据的,而在NIO中我们也使用了管道来进行数据的传输。

channel种类:
常用的channel为TCP channel,而tcp channel中分为socket channel和server socket channel,也就是客户端和服务端,下面的案例也使用该channel。
高并发编程基础——初识NIO_第3张图片

注意: channel默认为阻塞方式,所以在使用的时候可以手动切换为非阻塞,并且channel可以双向传输也就是客户端用channel传输数据到服务端接收,服务端可以用channel传输数据到客户端接收。

selector:

selector可以选择channel中的事件(write,read,accept),根据选中的不同事件来做不同的操作,需要注意的是selector只能对非阻塞的channel来进行选择。
高并发编程基础——初识NIO_第4张图片

综合案例:

服务端:
高并发编程基础——初识NIO_第5张图片

public class Server {

    public static void main(String[] args) throws IOException {

        // 开启服务器端通道
        ServerSocketChannel ssc = ServerSocketChannel.open();
        // 设置非阻塞
        ssc.configureBlocking(false);
        // 绑定端口
        ssc.bind(new InetSocketAddress(8090));
        // 开启选择器
        Selector selc = Selector.open();
        // 将通道注册到选择器上
        ssc.register(selc, SelectionKey.OP_ACCEPT);
        // 模拟:服务器开启之后不关闭
        while (true) {
            // 随着运行时间的延长,接收到的请求会越来越多
            // 需要针对这些请求进行选择,将能触发事件的请求留下
            // 将不能触发事件的请求过滤掉
            selc.select();
            // 选完之后,留下来的请求都是有用的请求
            // connect/read/write -> accept/write/read
            // 因为这些请求中不一定存在所有的事件
            // 所以需要获取请求的事件类型
            Set<SelectionKey> set = selc.selectedKeys();
            // 需要针对请求的不同类型来进行分门别类的处理
            Iterator<SelectionKey> it = set.iterator();
            while (it.hasNext()) {
                SelectionKey key = it.next();
                // 触发服务器的accept操作
                // -> 说明客户端一定调用了connect方法
                if (key.isAcceptable()) {
                    // 从事件中获取通道
                    ServerSocketChannel sscx =
                            (ServerSocketChannel) key.channel();
                    // 接收连接
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    // 根据需求确定,如果需要读操作,那么就给READ
                    // 如果需要写操作,那么就给WRITE
                    // 如果存在多个register,那么后边的会覆盖前边的
                    sc.register(selc,
                            //SelectionKey.OP_READ +  SelectionKey.OP_WRITE);
                            // SelectionKey.OP_READ |  SelectionKey.OP_WRITE);
                            SelectionKey.OP_READ ^ SelectionKey.OP_WRITE);
                }
                if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    // 读取数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    sc.read(buffer);
                    System.out.println(new String(buffer.array(), 0,
                            buffer.position()));
                    // 读取完成之后,需要将READ事件从通道身上移除掉
                    // key.interestOps() - 获取到所有事件
                    sc.register(selc,
                            // key.interestOps() - SelectionKey.OP_READ);
                            key.interestOps() ^ SelectionKey.OP_READ);
                }
                if (key.isWritable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    sc.write(ByteBuffer.wrap("收到数据啦~~~".getBytes()));
                    sc.register(selc,
                            key.interestOps() - SelectionKey.OP_WRITE);
                }
                // 处理完成之后,需要将这一大类事件移除掉
                it.remove();
            }
        }

    }

}

客户端:

public class Client {

    public static void main(String[] args) throws IOException {

        SocketChannel sc = SocketChannel.open();
        sc.connect(
                new InetSocketAddress("localhost", 8090));
        sc.write(ByteBuffer.wrap("hello server".getBytes()));
        // 读取数据
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        sc.read(buffer);
        System.out.println(
                new String(buffer.array(), 0, buffer.position()));
        sc.close();
    }

}

你可能感兴趣的:(高并发编程基础——初识NIO)