Java NIO

简介:

Java是非常繁杂的语言, 比如IO就是典型的代表...

Java IO

首先1.0, 是基于8位字节流的InputStream和OutputStream系列 
然后是1.1, 是基于16位的字符流(unicode)的Reader和Writer系列

下表是对应关系, 其中InputStreamReader和OutputStreamWriter, 起到两个系列之间的适配作用

当然实际的继承关系比这个复杂的多, 通过装饰模式, 产生各种IO类, 非常繁杂

image

 

Java NIO, New IO

从1.4开始, Java提供new IO 
其实在new IO中主要两个提升 
a, buffer和channel 
解决小块数据传输带来的效率问题, 引入buffer数据结构, 可以批量传输以提高效率(这样更符合底层数据传输的方式), 一个挖煤的比喻, 从挖一铲运一铲到挖满一卡车再运出来

b, 对于socket channel加入非阻塞方式

 

Buffer

提供一种支持丰富操作的数据结构, 同时虽然对于所有的类型(除bool类型)都有相应的buffer, 但是只有ByteBuffer可以直接被channel读取, 其余的都需要在放到channel之前做类型转换

image

数据结构

image

支持操作

image

下图以FileChannel为例子, 
首先Channel只能接收ByteBuffer作为write/read的参数 
对于其他的buffer必须做类型转换, 尤其对于CharBuffer需要考虑charset的encode/decode

image

 

Channel

管道很形象, 就是连接发送端和接收端之间的媒介 
其实就是对于传统socket或file接口针对ByteBuffer的封装

I/O可以分为广义的两大类别:File I/O和Stream I/O 
所以对应的, Channel分为两类, FileChannel和Socket相关channel(SocketChannel、ServerSocketChannel和 DatagramChannel)

FileChannel

FileChannel类可以实现常用的read,write以及scatter/gather操作(对于多个buffer的批处理), 同时它也提供了很多专用于文件的新方法. 
文件通道总是阻塞式的, 因为现代操作系统都有复杂的缓存和预取机制, 所以本地磁盘I/O操作延迟很少

FileChannel对象不能直接创建。一个FileChannel实例只能通过在一个打开的file对象(RandomAccessFile、FileInputStream或 FileOutputStream)上调用getChannel( )方法获取

FileChannel对象是线程安全(thread-safe)

1.4中, Java通过FileChannel实现了文件锁(之前Java不支持文件锁), 但是这是进程级别锁, 相同进程中不同线程无法通过文件锁进行互斥

SocketChannel

新的socket通道类可以运行非阻塞模式, 这个大大提升了Java IO的性能, 之前只能使用多线程阻塞的方式来处理并发, 但线程调度的开销也很高尤其当维护大量线程的时候

全部socket通道类(DatagramChannel、SocketChannel和ServerSocketChannel), 分别对应于java.net中的(Socket、ServerSocket和DatagramSocket), 并且Channel其实就是对他们的封装

ServerSocketChannel

ByteBuffer buffer = ByteBuffer.wrap (GREETING.getBytes( )); 
ServerSocketChannel ssc = ServerSocketChannel.open( ); 
ssc.socket( ).bind (new InetSocketAddress (port)); 
ssc.configureBlocking (false); //non-blocking

while (true) { 
    System.out.println ("Waiting for connections"); 
    SocketChannel sc = ssc.accept( ); //不会blocking直接返回
    if (sc == null) { 
        // no connections, snooze a while 
        Thread.sleep (2000); } 
    else { 
        System.out.println ("Incoming connection from: " + sc.socket().getRemoteSocketAddress( ));  
        buffer.rewind( ); 
        sc.write (buffer); 
        sc.close( ); } } 

 

SocketChannel

InetSocketAddress addr = new InetSocketAddress (host, port); 
SocketChannel sc = SocketChannel.open( ); 
sc.configureBlocking (false); //设置non-blocking
sc.connect (addr); //不会阻塞等待
while ( ! sc.finishConnect( )) { 
    doSomethingElse( ); } 
doSomethingWithChannel (sc); sc.close( ); 

从上面两个例子可以看出channel的使用, 其实和原来的API没有很大的不同, 关键就是支持non-blocking方式

 

Selector

当然还需要selector, 不然非阻塞意义不大, 象C/C++中的select, poll

Selector selector = Selector.open( ); 
channel1.register (selector, SelectionKey.OP_READ); 
channel2.register (selector, SelectionKey.OP_WRITE); 
channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); 
// Wait up to 10 seconds for a channel to become ready 
readyCount = selector.select (10000); 

只有继承SelectableChannel的Channel类才可以被注册到Selector对象上, 所以FileChannel对象不是可选择的, 而所有SocketChannel都是可选择的

SelectionKey

代表了Selector和SelectableChannel的注册关系 
key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定 
key.channel(); // 返回该SelectionKey对应的channel 
key.selector(); // 返回该SelectionKey对应的Selector 
key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask 
key.readyOps(); //返回一个bit mask,代表在相应channel上可以进行的IO操作

package java.nio.channels;
//ops, selector所关心的通道操作,读(read),写(write),连接(connect)和接受(accept)
public abstract SelectionKey register (Selector sel, int ops) throws ClosedChannelException; 

public abstract class SelectionKey { 
    public static final int OP_READ; 
    public static final int OP_WRITE; 
    public static final int OP_CONNECT; 
    public static final int OP_ACCEPT; 
    public abstract SelectableChannel channel( ); 
    public abstract Selector selector( ); 
    public abstract void cancel( ); 
    public abstract boolean isValid( ); 
    public abstract int interestOps( ); 
    public abstract void interestOps (int ops); 
    public abstract int readyOps( ); 
    public final boolean isReadable( ); 
    public final boolean isWritable( );
    public final boolean isConnectable( );
    public final boolean isAcceptable( ); 
    public final Object attach (Object ob) 
    public final Object attachment( ) } 

使用的代码

// Allocate an unbound server socket channel 
ServerSocketChannel serverChannel = ServerSocketChannel.open(); 
// Get the associated ServerSocket to bind it with ServerSocket 
serverSocket = serverChannel.socket(); 
// Create a new Selector for use below Selector 
selector = Selector.open(); 
// Set the port the server channel will listen to 
serverSocket.bind(new InetSocketAddress(port)); 
// Set nonblocking mode for the listening socket 
serverChannel.configureBlocking(false);
// Register the ServerSocketChannel with the Selector 
serverChannel.register(selector, SelectionKey.OP_ACCEPT); //关注accept

while (true) { 
// This may block for a long time. Upon returning, the 
// selected set contains keys of the ready channels. 
int n = selector.select(); 
if (n == 0) { 
    continue; // nothing to do
}
// Get an iterator over the set of selected keys 
Iterator it = selector.selectedKeys().iterator(); 
// Look at each key in the selected set 
while (it.hasNext()) { 
    SelectionKey key = (SelectionKey) it.next(); 
    // Is a new connection coming in? 
    if (key.isAcceptable()) { 
        ServerSocketChannel server = (ServerSocketChannel) key.channel(); 
        SocketChannel channel = server.accept(); 
        registerChannel(selector, channel, SelectionKey.OP_READ); //Accept后设置成关注Read
        sayHello(channel); } 
    // Is there data to read on this channel? 
    if (key.isReadable()) { 
        readDataFromSocket(key); } 
} } } 


本文章摘自博客园,原文发布日期:2013-10-10
目录
相关文章
|
1月前
|
存储 Java 数据处理
|
1月前
|
Java API
java中IO与NIO有什么不同
java中IO与NIO有什么不同
|
7天前
|
监控 Java 开发者
深入理解 Java 网络编程和 NIO
【4月更文挑战第19天】Java网络编程基于Socket,但NIO(非阻塞I/O)提升了效率和性能。NIO特点是非阻塞模式、选择器机制和缓冲区,适合高并发场景。使用NIO涉及通道、选择器和事件处理,优点是高并发、资源利用率和可扩展性,但复杂度、错误处理和性能调优是挑战。开发者应根据需求选择是否使用NIO,并深入理解其原理。
|
2月前
|
移动开发 编解码 网络协议
用Java的BIO和NIO、Netty来实现HTTP服务器(三) 用Netty实现
用Java的BIO和NIO、Netty来实现HTTP服务器(三) 用Netty实现
|
2月前
|
网络协议 Java Linux
用Java来实现BIO和NIO模型的HTTP服务器(二) NIO的实现
用Java来实现BIO和NIO模型的HTTP服务器(二) NIO的实现
|
2月前
|
编解码 网络协议 Java
用Java的BIO和NIO、Netty实现HTTP服务器(一) BIO与绪论
用Java的BIO和NIO、Netty实现HTTP服务器(一) BIO与绪论
|
3月前
|
Java 应用服务中间件 Linux
java中的NIO,BIO,AIO
java中的NIO,BIO,AIO
17 0
|
3月前
|
设计模式 网络协议 Java
Java NIO 网络编程 | Netty前期知识(二)
Java NIO 网络编程 | Netty前期知识(二)
77 0
|
3月前
|
Java 索引
📌 Java NIO Buffer
Java NIO缓冲区在与NIO通道交互时使用。数据从通道读取到缓冲区,然后从缓冲区写入通道。 缓冲区本质上是一块内存,可以在其中写入数据,然后再进行读取。这个内存块被封装在一个NIOBuffer对象中,该对象提供了一组方法,可以更容易地使用内存块。
|
3月前
|
缓存 网络协议 Java
📌 Java NIO Channel
Java NIOChannel和传统的流相似,但是也存在一些差异: • 在同一个Channel通道中,既可以进行 读操作 也可以进行 写操作,但是 流 只能进行 读 或者 写 其中一种操作。 • Channel通道可以进行异步读写。 • Channel可以从 Buffer中进行读写操作。将数据从Channel通道读取到Buffer缓冲区,并将数据从Buffer缓冲区写入Channel通道。