JAVA NIO详解

本文深入探讨Java NIO(非阻塞I/O),解释其如何通过Selector管理多个Channel,减少线程开销。讲解了Buffer的使用步骤和关键属性,以及Scatter/Gather技术。同时介绍了文件锁的概念和NIO管道在多线程通信中的作用。最后,阐述了Java NIO在实际应用中的重要性和优势。
摘要由CSDN通过智能技术生成

一、Java NIO

1.1阻塞IO概述

通常在进行同步I/О操作时,如果读取数据,代码会阻塞直至有可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的Server/Client模式会基于TPR(Thread per Request),服务器会为每个客戌端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型,并设置线程池线程的最大数量,这由带来了新的问题,如果线程池中有100个线程,而有100个用户都在进行大文件下载,会导致第101个用户的请求无法及时处理,即便第101个用户只想请求一个几KB大小的页面。在这里插入图片描述

1.2 NIO(非阻塞IO)概述
在这里插入图片描述

  • 当有读或写的注册事件发生,可以从Selector中获取相应的SelectionKey,同时可以从SelectionKey中找到发生的时间和发生的具体的SelectableChannel。
  • 非阻塞指的是IO事件本身不阻塞,但是获取IO事件的select()方法是需要阻塞等待的.区别是阻塞的IO会阻塞在IO操作上, NIO阻塞在事件获取上,没有事件就没有IO,从高层次看IO就不阻塞了.也就是说只有IO已经发生那么我们才评估IO是否阻塞,但是select()阻塞的时候IO还没有发生,何谈IO的阻塞呢?NIO的本质是延迟IO操作到真正发生IO的时候,而不是以前的只要IO流打开了就一直等待IO操作。

1.2.1JAVA NIO 组成

Channel

  • Channel是一个通道,可以通过它读取和写入数据,它就像水管一样,网络数据通过Channel读取和写入。通道与流的不同之处在于通道是双向的,流只是在一个方向上移动(一个流必须是 InputStream 或者OutputStream的子类),而且通道可以用于读、写或者同时用于读写。因为Channel是全双工的,所以它可以比流更好地映射底层操作系统的APl。
  • NIO中通过channel封装了对数据源的操作,通过channel我们可以操作数据源,但又不必关心数据源的具体物理结构。这个数据源可能是多种的。比如,可以是文件,也可以是网络socket。在大多数应用中,channel与文件描述符或者socket是——对应的。Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。

分类

  • FileChannel从文件中读写数据。
  • DatagramChannel能通过UDP读写网络中的数据。
  • SocketChannel 能通过TCP读写网络中的数据。’
  • ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

1.2.2 Scatter/Gather

分散(Scatter)

  • 从Channel中读取数据,数据分散到多个buffer中去。

聚集(Gather)

  • 将多个buffer中的数据聚集到一个channel中去。

2.1 Buffer

缓冲区。本质上为可以读取数据,也可以写入数据的一块内存。

在这里插入图片描述

2.1.1 使用Buffer步骤

  • 写数据到Buffer
  • 调用filp()方法
  • 从Buffer读取数据
  • 调用clear()方法或compact()方法 (clear会清空缓冲区,compact会清空已经读过的数据)

2.1.2 Buffer的三个属性

(1) Capacity

  • 内存块的大小,一旦Buffer满了,需要清空。

(2) Position

  • 写模式中,position表示当前写入的位置,position最大为capacity-1。读模式下,为读入数据的当前位置。

(3) limit

  • 写数据时,能写入多少的数据,写模式下,limit等于capacity。读数据时,表示有多少数据可读,在写模式下就是position。

2.2 缓冲区分片(根据现有缓冲区分片多个子缓冲区)

  • 只读缓冲区:只能读数据,不能写入。
  • 直接缓冲区:为了加快I/O速度。
  • 内存映射文件I/O。

3.1 Selector

  • 可以使用单线程管理多个channel,也就是管理多个网络连接,selector相当于多路复用器,使用更少的线程,相比使用多个线程,避免上下文切换。
  • 一些channel是不可被复用,例如filechannel,只有继承了SelectorableChannel才能被复用。

3.1.2 channel注册到Selector

  • 注册
    Channel.resiter(Selector sel,int ops)
    sel为选择器,ops为需要查询的通道的操作(可读,可写,连接,接收)
  • 选择键
    (1) Channel注册到后,并且一旦通道处于某种就绪的状态,就可以被选择器查询到。这个工作,使用选择器Selector的select ()方法完成。select方法的作用,对感兴趣的通道操作,进行就绪状态的查询。
    (2) Selector可以不断的查询Channel中发生的操作的就绪状态。并且挑选感兴趣的操作就绪状态。一旦通道有操作的就绪状态达成,并且是Selector感兴趣的操作,就会被Selector选中,放入选择键集合中。
    (3)一个选择键,首先是包含了注册在Selector的通道操作的类型,比方说SelectionKey.OP.READ。也包含了特定的通道与特定的选择器之间的注册关系。开发应用程序是,选择键是编程的关键。NIO的编程,就是根据对应的选择键,进行不同的业务逻辑处理。

3.2 NIO管道(pipe)

Java NIO管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。

>

3.3 文件锁(FileLock)

  • 文件锁是进程级别的,不是线程级别的,不能解决多线程同时访问一个文件的,修改同一文件的问题。同一个进程,多个线程能够同时访问。
  • 文件锁是程序所属JVM所持有,要调用release()或者关闭对应的FileChannel对象或者程序退出,才会释放对应的文件锁。

具体代码在https://github.com/guopeizhun/java-nio

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值