java nio

本文深入探讨Java NIO的实现机制,包括非阻塞IO的基本原理、核心组件Selector、SelectableChannel和SelectionKey的作用,以及如何利用它们实现高效的网络通信。

IO API的可伸缩性对Web应用有着极其重要的意义。Java 1.4版以前的API中,阻塞I/O令许多人失望。从J2SE 1.4版本开始,Java终于有了可伸缩的I/O API。本文分析并计算了新旧IO API在可伸缩性方面的差异。Java向Socket写入数据时必须调用关联的OutputStream的write()方法。只有当所有的数据全部写入时,write()方法调用才会返回。倘若发送缓冲区已满且连接速度很低,这个调用可能需要一段时间才能完成。如果程序只使用单一的线程,其他连接就必须等待,即使那些连接已经做好了调用write()的准备也一样。为了解决这个问题,你必须把每一个Socket和一个线程关联起来;采用这种方法之后,当一个线程由于I/O相关的任务被阻塞时,另一个线程仍旧能够运行。

尽管线程的开销不如进程那么大,但是,考虑到底层的操作平台,线程和进程都属于消耗大量资源的程序结构。每一个线程都要占用一定数量的内存,而且除此之外,多个线程还意味着线程上下文的切换,而这种切换也需要昂贵的资源开销。因此,Java需要一个新的API来分离Socket与线程之间过于紧密的联系。

 

java非阻塞IO package

java.nio

java.nio.channels

java.nio.charset

非阻塞IO实现reactor模式,通过事件机制允许应用程序同时监控多个channel以提高性能,这一功能是通过Selector,SelectableChannel和SelectionKey这3个类来实现的。

SelectableChannel代表了可以支持非阻塞IO操作的channel,可以将其注册在Selector上,这种注册的关系SelectionKey这个类来表现。Selector这个类通过select()函数,给应用程序提供了一个可以同时监控多个IO channel的方法:
应用程序通过调用select()函数,让Selector监控注册在其上的多个SelectableChannel,当有channel的IO操作发生时,select()方法就会返回以让应用程序检查channel的状态,并作相应的处理。
下面是JDK 1.4中非阻塞IO的一个例子,这段code使用了非阻塞IO实现了一个time server:

    private static void acceptConnections(int port) throws Exception {
       // 打开一个Selector
       Selector acceptSelector =
           SelectorProvider.provider().openSelector();
       // 创建一个ServerSocketChannel,这是一个SelectableChannel的子类
       ServerSocketChannel ssc = ServerSocketChannel.open();
       // 将其设为non-blocking状态,这样才能进行非阻塞IO操作
       ssc.configureBlocking(false);
       // 给ServerSocketChannel对应的socket绑定IP和端口
       InetAddress lh = InetAddress.getLocalHost();
       InetSocketAddress isa = new InetSocketAddress(lh, port);
       ssc.socket().bind(isa);
       // 将ServerSocketChannel注册到Selector上,返回对应的SelectionKey
       SelectionKey acceptKey =
           ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
       int keysAdded = 0;
       // 用select()函数来监控注册在Selector上的SelectableChannel
       // 返回值代表了有多少channel可以进行IO操作 (ready for IO)
       while ((keysAdded = acceptSelector.select()) > 0) {
           // selectedKeys()返回一个SelectionKey的集合,
           // 其中每个SelectionKey代表了一个可以进行IO操作的channel。
           // 一个ServerSocketChannel可以进行IO操作意味着有新的TCP连接连入了
           Set readyKeys = acceptSelector.selectedKeys();
           Iterator i = readyKeys.iterator();
           while (i.hasNext()) {
              SelectionKey sk = (SelectionKey) i.next();
              // 需要将处理过的key从selectedKeys这个集合中删除
              i.remove();
              // 从SelectionKey得到对应的channel
              ServerSocketChannel nextReady =
                  (ServerSocketChannel) sk.channel();
              // 接受新的TCP连接
              Socket s = nextReady.accept().socket();
              // 把当前的时间写到这个新的TCP连接中
              PrintWriter out =
                  new PrintWriter(s.getOutputStream(), true);
              Date now = new Date();
              out.println(now);
              // 关闭连接
              out.close();
           }
       }
    }

 

这是个纯粹用于演示的例子,因为只有一个ServerSocketChannel需要监控,所以其实并不真的需要使用到非阻塞IO。不过正因为它的简单,可以很容易地看清楚非阻塞IO是如何工作的。
SelectableChannel
这个抽象类是所有支持非阻塞IO操作的channel(如DatagramChannel、SocketChannel)的父类。SelectableChannel可以注册到一个或多个Selector上以进行非阻塞IO操作。
SelectableChannel可以是blocking和non-blocking模式(所有channel创建的时候都是blocking模式),只有non-blocking的SelectableChannel才可以参与非阻塞IO操作。

Selector中的注册方法:

public abstract class AbstractSelector extends Selector {

    protected abstract SelectionKey register(AbstractSelectableChannel ch,  int ops, Object att);

    。。。

}

 将当前channel注册到一个Selector上并返回对应的SelectionKey。在这以后,通过调用Selector的select()函数就可以监控这个channel。ops这个参数是一个bit mask,代表了需要监控的IO操作。

 

 

Channel中的注册方法:

public abstract class AbstractSelectableChannel extends SelectableChannel{

 

    public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException
    {
 if (!isOpen())
     throw new ClosedChannelException();
 if ((ops & ~validOps()) != 0)
     throw new IllegalArgumentException();
 synchronized (regLock) {
     if (blocking)
  throw new IllegalBlockingModeException();
     SelectionKey k = findKey(sel);
            if (k != null) {
                k.interestOps(ops);
  k.attach(att);
            }
     if (k == null) {
  // New registration
  k = ((AbstractSelector)sel).register(this, ops, att);
  addKey(k);
     }
            return k;
        }
    }

 

。。。

}

通过register()方法,SelectableChannel可以注册到Selector上。

 

总之,就是selectAbleChannel注册到Selector上,可以被selector监控,观察者、事件机制。

 

 

 

 

Selector open() ,是Selector的一个静态方法,用于创建实例。

SelectionKey 代表了Selector和SelectableChannel的注册关系。
在一个Selector中,有3个SelectionKey的集合:
1.key set代表了所有注册在这个Selector上的channel,这个集合可以通过keys()方法拿到。
2.Selected-key set代表了所有通过select()方法监测到可以进行IO操作的channel,这个集合可以通过selectedKeys()拿到。
3.Cancelled-key set代表了已经cancel了注册关系的channel,在下一个select()操作中,这些channel对应的SelectionKey会从key set和cancelled-key set中移走。这个集合无法直接访问。

 

 

Selector定义了4个静态常量来表示4种IO操作,这些常量可以进行位操作组合成一个bit mask。

int OP_ACCEPT
有新的网络连接可以accept,ServerSocketChannel支持这一非阻塞IO。
int OP_CONNECT
代表连接已经建立(或出错),SocketChannel支持这一非阻塞IO。
int OP_READ
int OP_WRITE
代表了读、写操作。

 

java doc:

SocketChannel针对面向流的连接套接字的可选择通道(client),

ServerSocketChannel针对面向流的侦听套接字的可选择通道(server),两者都集成自SelectableChannel。

 

可通过 Selector 实现多路复用的通道。

为了与选择器一起使用,此类的实例必须首先通过 register 方法进行注册。此方法返回一个表示该通道已向选择器注册的新 SelectionKey 对象。

向选择器注册后,通道在注销 之前将保持注册状态。注销涉及释放选择器已分配给该通道的所有资源。

不能直接注销通道;相反,必须取消 表示通道注册的键。取消某个键要求在选择器的下一个选择操作期间注销通道。可通过调用某个键的 cancel 方法显式地取消该键。无论是通过调用通道的 close 方法,还是中断阻塞于该通道上 I/O 操作中的线程来关闭该通道,都会隐式地取消该通道的所有键。

如果选择器本身已关闭,则将注销该通道,并且表示其注册的键将立即无效。

一个通道至多只能在任意特定选择器上注册一次。

可通过调用 isRegistered 方法来确定是否向一个或多个选择器注册了某个通道。

多个并发线程可安全地使用可选择的通道。 isBlocking 方法来确定其阻塞模式。

阻塞模式

可选择的通道要么处于阻塞 模式,要么处于非阻塞 模式。在阻塞模式中,每一个 I/O 操作完成之前都会阻塞在其通道上调用的其他 I/O 操作。在非阻塞模式中,永远不会阻塞 I/O 操作,并且传输的字节可能少于请求的数量,或者可能根本不传输字节。可通过调用可选择通道的

新创建的可选择通道总是处于阻塞模式。在结合使用基于选择器的多路复用时,非阻塞模式是最有用的。向选择器注册某个通道前,必须将该通道置于非阻塞模式,并且在注销之前可能无法返回到阻塞模式。

 

SelectionKey表示 SelectableChannelSelector 中的注册的标记。

每次向选择器注册通道时就会创建一个选择键。通过调用某个键的 cancel 方法、关闭其通道,或者通过关闭其选择器来取消 该键之前,它一直保持有效。取消某个键不会立即从其选择器中移除它;相反,会将该键添加到选择器的已取消键集,以便在下一次进行选择操作时移除它。可通过调用某个键的 isValid 方法来测试其有效性。

 

继续学习

nio与不同内核实现的绑定epoll、iocp ,reactor模式,深入了解原理。

netty, mina实现。

JAVA NIO是针对多连接并发的高效处理,并不一定能够提高单个io的处理速度,技术的适用场景。

 

 

 

【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其与遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究与改进中。
11-13
Java NIO(New I/O)是Java 1.4引入的新的I/O API,用于替代标准的Java I/O API。它提供了非阻塞I/O操作,能显著提高程序的性能和可扩展性,尤其适用于处理大量并发连接的场景。 ### 核心组件 - **Channel(通道)**:Channel是对传统I/O中流的模拟,用于在缓冲区和实体(如文件、套接字)之间传输数据。常见的Channel实现有FileChannel、SocketChannel、ServerSocketChannel和DatagramChannel等。例如,FileChannel用于文件读写,SocketChannel用于TCP网络通信。 - **Buffer(缓冲区)**:Buffer是一个用于存储特定基本类型数据的容器。所有的缓冲区都是Buffer抽象类的子类,如ByteBuffer、CharBuffer、IntBuffer等。使用时,数据先被写入Buffer,再从Buffer读取到Channel,反之亦然。 - **Selector(选择器)**:Selector是Java NIO实现非阻塞I/O的关键。它允许一个线程处理多个Channel的I/O事件。通过将多个Channel注册到一个Selector上,Selector可以不断轮询这些Channel,当某个Channel有可用的I/O操作时,就会被Selector选中,从而实现单线程处理多个连接的目的。 - **SelectionKey(选择键)**:SelectionKey用于维护Selector和SelectableChannel的关系,每个Channel注册到Selector时都会产生一个SelectionKey,它聚合了Channel和Selector,有点类似EventKey。通过SelectionKey可以获取对应的Channel和Selector,还可以设置和查询感兴趣的I/O事件类型,如读、写、连接和接受连接事件等 [^1]。 ### 使用指南 #### 1. 使用FileChannel进行文件读写 ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileChannelExample { public static void main(String[] args) { try (FileInputStream fis = new FileInputStream("input.txt"); FileOutputStream fos = new FileOutputStream("output.txt"); FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel()) { ByteBuffer buffer = ByteBuffer.allocate(1024); while (inChannel.read(buffer) != -1) { buffer.flip(); // 切换为读模式 outChannel.write(buffer); buffer.clear(); // 清空缓冲区,准备下一次写入 } } catch (IOException e) { e.printStackTrace(); } } } ``` #### 2. 使用Selector实现非阻塞网络编程 ```java import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; public class NioServerExample { public static void main(String[] args) { try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); Selector selector = Selector.open()) { serverSocketChannel.socket().bind(new InetSocketAddress(8080)); serverSocketChannel.configureBlocking(false); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { int readyChannels = selector.select(); if (readyChannels == 0) continue; Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectedKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = serverChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } else if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int bytesRead = socketChannel.read(buffer); if (bytesRead > 0) { buffer.flip(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); System.out.println(new String(data)); } } keyIterator.remove(); } } } catch (IOException e) { e.printStackTrace(); } } } ``` ### 原理 Java NIO的非阻塞I/O原理基于操作系统的I/O多路复用机制。在传统的阻塞I/O模型中,一个线程只能处理一个连接,当线程在等待某个连接的数据时会被阻塞,无法处理其他连接。而在Java NIO中,Selector利用操作系统提供的I/O多路复用功能,如Linux的select、poll和epoll,通过一个线程监控多个Channel的I/O状态。当某个Channel有数据可读或可写时,Selector会感知到并通知应用程序进行相应的处理,从而实现单线程处理多个连接,提高了系统的并发处理能力。 ### 应用场景 - **网络编程**:在构建高性能的网络服务器时,如Web服务器、聊天服务器、游戏服务器等,Java NIO的非阻塞I/O特性可以显著减少线程数量,降低系统资源消耗,提高服务器的并发处理能力。 - **文件处理**:对于大文件的读写操作,使用FileChannel和ByteBuffer可以提高文件读写的效率,尤其是在需要随机访问文件内容时。 - **实时数据处理**:在处理实时数据流时,如视频流、音频流等,Java NIO可以高效地处理数据的传输和处理,确保数据的实时性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值