java nio
推荐阅读:
javaNIO非阻塞式服务器
深入理解Java NIO
java SPI
网络编程释疑之:同步,异步,阻塞,非阻塞
java NIO、BIO、AIO全面剖析
例子:
NIO服务器的简单实现
阻塞就是应用程序来读取但是却读不到东西,所以阻塞在这里,不停地询问操作系统,直到操作系统通知应用程序可读。这时候是从操作系统的缓存中拿到数据。拿到的是完整的数据报文。
操作系统只有在拿到所有的数据包的时候,才会通知应用程序有东西可读。
NIO的3个重要角色
Buffer;
Selector;
Channel;
java.nio.channels.ServerSocketChannel
- 所有已实现的接口
- AutoCloseable(JDK1.7可自动关闭,垃圾回收的时候会尝试关闭)
- Closeable(可关闭的),
- Channel(用于 I/O 操作的连接),
- InterruptibleChannel(可被异步关闭和中断的通道)
- NetworkChannel(到网络套接字的通道)
父类
- AbstractSelectableChannel(可选择通道的基本实现类)
- SelectableChannel(可通过 Selector 实现多路复用的通道)
- AbstractInterruptibleChannel(可中断通道的基本实现类)
默认实现类sun.nio.ch.ServerSocketChannelImpl(可通过java的SPI实现自己的ServerSocketChannelImpl,具体可以看 java.nio.channels.spi.SelectorProvider)
java.nio.channels.SocketChannel
- 所有已实现的接口
- AutoCloseable(JDK1.7可自动关闭,垃圾回收的时候会尝试关闭)
- Closeable(可关闭的),
- Channel(用于 I/O 操作的连接),
- ByteChannel(可读取和写入字节的信道,只是统一了 ReadableByteChannel 和 WritableByteChannel;它没有指定任何新操作。)
- ScatteringByteChannel(可将字节读入缓冲区序列的通道。分散 读取操作可在单个调用中将一个字节序列读入一个或多个给定的缓冲区序列。分散读取通常在实现网络协议或文件格式时很有用,例如将数据分组放入段中(这些段由一个或多个长度固定的头,后跟长度可变的正文组成)。在 GatheringByteChannel 接口中定义了类似的集中 写入操作。)
- GatheringByteChannel(可从缓冲区序列写入字节的通道。集中 写入操作可在单个调用中写入来自一个或多个给定缓冲区序列的字节序列。集中写入通常在实现网络协议或文件格式时很有用,例如将数据分组放入段中(这些段由一个或多个长度固定的头,后跟长度可变的正文组成)。在 ScatteringByteChannel 接口中定义了类似的分散 读取操作。)
- NetworkChannel(到网络套接字的通道)
- 父类
- AbstractSelectableChannel(可选择通道的基本实现类)
- SelectableChannel(可通过 Selector 实现多路复用的通道)
- AbstractInterruptibleChannel(可中断通道的基本实现类)
- 默认实现类sun.nio.ch.SocketChannelImpl(可通过java的SPI实现自己的ServerSocketChannelImpl,具体可以看 java.nio.channels.spi.SelectorProvider)
/**
* 用于 I/O 操作的连接 继承自AutoCloseable,Closeable
*/
public interface Channel extends Closeable {
//判断此通道是否处于打开状态。
public boolean isOpen();
/*
关闭此通道。
关闭某个通道后,试图对其调用 I/O 操作就会导致 ClosedChannelException 被抛出。
如果此通道已经关闭,则调用此方法无效。
可在任意时间调用此方法。但是如果其他某个线程已调用此方法,那么在第一个调用完成前另一个调用将被阻塞,之后该方法将返回,不受任何影响。
*/
public void close() throws IOException;
}
/**
* 可被异步关闭和中断的通道
* 如果某个线程阻塞于可中断通道上的 I/O 操作中,则另一个线程可调用该通道的 close 方法。
* 这将导致已阻塞线程接收到 AsynchronousCloseException。
* 如果某个线程阻塞于可中断通道上的 I/O 操作中,则另一个线程可调用该阻塞线程的 interrupt 方法。
* 这将导致该通道被关闭,已阻塞线程接收到 ClosedByInterruptException,并且设置已阻塞线程的中断状态。
*/
public interface InterruptibleChannel extends Channel {
//关闭此通道。所有当前阻塞于此通道上的 I/O 操作中的线程都将接收到 AsynchronousCloseException。
public void close() throws IOException;
}
/**
* NetworkChannel是一个通道到网络socket的实现,#bind用于绑定一个本地socket地址,
* #getLocalAddress可以返回绑定的地址,#setOption和#getOption用于设置和获取网络通道的选项配置。
* 接口的实现应该精确支持的选项配置集。
* #bind和#setOption在调用时,不一定非要返回网络通道,允许被链式调用。网络通道的
* 具体实现应该明确#bind和#setOption的返回类型,以便可以链式调用。
*/
public interface NetworkChannel extends Channel {
/*
绑定通道socket到本地地址
bind的方法用于建立socket和本地地址的关系。只要关系建立,在通道没关闭之前socket
仍绑定着本地SocketAddress。如果socket地址参数为null,默认socket将绑定自动分配的地址
AlreadyBoundException 已绑定
UnsupportedAddressTypeException 不支持地址类型
ClosedChannelException 通道关闭
IOException 操作异常
SecurityException 权限访问异常
*/
NetworkChannel bind(SocketAddress local) throws IOException;
//返回socket通道绑定的socket地址,如果为null,即没有绑定。
// 如果通道绑定到一个网络协议socket地址,将会返回一个InetSocketAddress的地址
SocketAddress getLocalAddress() throws IOException;
//设置socket选项值
<T> NetworkChannel setOption(SocketOption<T> name, T value) throws IOException;
//获取socket选项的值
<T> T getOption(SocketOption<T> name) throws IOException;
//返回通道支持的socket选项 即使在通道关闭时,此方法仍返回socket配置选项集
Set<SocketOption<?>> supportedOptions();
}
/**
* 可中断通道的基本实现类。
*/
public abstract class AbstractInterruptibleChannel implements Channel, InterruptibleChannel
{
// -- Interruption machinery --
private Interruptible interruptor;
private volatile Thread interrupted;
private final Object closeLock = new Object();
private volatile boolean open = true;
//关闭此通道。
public final void close() throws IOException {
synchronized (closeLock) {
if (!open)
return;
open = false;
implCloseChannel();
}
}
//关闭此通道。(由实现类具体实现)
protected abstract void implCloseChannel() throws IOException;
//判断此通道是否处于打开状态。
public final boolean isOpen() {return open;}
// 标记可能无限期阻塞的 I/O 操作的开始。
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (!open)
return;
open = false;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}
//标记可能无限期阻塞的 I/O 操作的结尾。
protected final void end(boolean completed) throws AsynchronousCloseException
{
blockedOn(null);
Thread interrupted = this.interrupted;
if (interrupted != null && interrupted == Thread.currentThread()) {
interrupted = null;
throw new ClosedByInterruptException();
}
if (!completed && !open)
throw new AsynchronousCloseException();
}
// -- sun.misc.SharedSecrets --
static void blockedOn(Interruptible intr) { // package-private
sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), intr);
}
}
/**
* 可通过 Selector 实现多路复用的通道。
* 为了与选择器一起使用,此类的实例必须首先通过 register 方法进行注册。此方法返回一个表示该通道已向选择器注册的新 SelectionKey 对象。
* 向选择器注册后,通道在注销 之前将保持注册状态。注销涉及释放选择器已分配给该通道的所有资源。
* 不能直接注销通道;相反,必须取消 表示通道注册的键。取消某个键要求在选择器的下一个选择操作期间注销通道。
* 可通过调用某个键的 cancel 方法显式地取消该键。无论是通过调用通道的 close 方法,还是中断阻塞于该通道上 I/O 操作中的线程来关闭该通道,都会隐式地取消该通道的所有键。
* 如果选择器本身已关闭,则将注销该通道,并且表示其注册的键将立即无效。
* 一个通道至多只能在任意特定选择器上注册一次。
* 可通过调用 isRegistered 方法来确定是否向一个或多个选择器注册了某个通道。
* 多个并发线程可安全地使用可选择的通道。
* 可选择的通道要么处于阻塞 模式,要么处于非阻塞 模式。在阻塞模式中,每一个 I/O 操作完成之前都会阻塞在其通道上调用的其他 I/O 操作。
* 在非阻塞模式中,永远不会阻塞 I/O 操作,并且传输的字节可能少于请求的数量,或者可能根本不传输字节。可通过调用可选择通道的 isBlocking 方法来确定其阻塞模式。
* 新创建的可选择通道总是处于阻塞模式。在结合使用基于选择器的多路复用时,非阻塞模式是最有用的。
* 向选择器注册某个通道前,必须将该通道置于非阻塞模式,并且在注销之前可能无法返回到阻塞模式。
*/
public abstract class SelectableChannel extends AbstractInterruptibleChannel implements Channel {
//返回创建此通道的提供者。
public abstract SelectorProvider provider();
//返回一个操作集,标识此通道所支持的操作。
public abstract int validOps();
//判断此通道当前是否已向任何选择器注册。
public abstract boolean isRegistered();
//获取表示通道向给定选择器注册的键。
public abstract SelectionKey keyFor(Selector sel);
//向给定的选择器注册此通道,返回一个选择键。
public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;
//向给定的选择器注册此通道,返回一个选择键。/
public final SelectionKey register(Selector sel, int ops) throws ClosedChannelException {
return register(sel, ops, null);
}
//调整此通道的阻塞模式。
public abstract SelectableChannel configureBlocking(boolean block) throws IOException;
//判断此通道上的每个 I/O 操作在完成前是否被阻塞。
public abstract boolean isBlocking();
//获取其 configureBlocking 和 register 方法实现同步的对象
public abstract Object blockingLock();
}
java.nio.channels.Selector
- 所有已实现的接口
- AutoCloseable(JDK1.7可自动关闭,垃圾回收的时候会尝试关闭)
- Closeable(可关闭的)
- 默认实现类sun.nio.ch.KQueueSelectorImpl
/**
* SelectableChannel 对象的多路复用器
* 可通过调用此类的 open 方法创建选择器,该方法将使用系统的默认选择器提供者创建新的选择器。
* 也可通过调用自定义选择器提供者的 openSelector 方法来创建选择器。通过选择器的 close 方法关闭选择器之前,它一直保持打开状态。
* 通过 SelectionKey 对象来表示可选择通道到选择器的注册。选择器维护了三种选择键集:
* 键集 包含的键表示当前通道到此选择器的注册。此集合由 keys 方法返回。
* 已选择键集 是这样一种键的集合,即在前一次选择操作期间,检测每个键的通道是否已经至少为该键的相关操作集所标识的一个操作准备就绪。
* 此集合由 selectedKeys 方法返回。已选择键集始终是键集的一个子集。
* 已取消键集 是已被取消但其通道尚未注销的键的集合。不可直接访问此集合。已取消键集始终是键集的一个子集。
*/
public abstract class Selector implements Closeable {
//打开一个选择器。通过调用系统级默认 SelectorProvider 对象的 openSelector 方法来创建新的选择器。
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
//告知此选择器是否已打开。
public abstract boolean isOpen();
//返回创建此通道的提供者。
public abstract SelectorProvider provider();
//返回此选择器的键集。
// 不可直接修改键集。仅在已取消某个键并且已注销其通道后才移除该键。试图修改键集会导致抛出 UnsupportedOperationException。
// 键集是非线程安全的。
public abstract Set<SelectionKey> keys();
//返回此选择器的已选择键集。
//可从已选择键集中移除键,但是无法直接添加键。试图向该键集中添加对象会导致抛出 UnsupportedOperationException。
// 已选择键集是非线程安全的。
public abstract Set<SelectionKey> selectedKeys();
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
//此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。
//调用此方法会清除所有以前调用 wakeup 方法所得的结果。
//返回 由选择操作更新其准备就绪操作集的键的数目,该数目可能为零
public abstract int selectNow() throws IOException;
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
//此方法不提供实时保证:它安排了超时时间,就像调用 Object.wait(long) 方法一样。
//参数 timeout - 如果为正,则在等待某个通道准备就绪时最多阻塞 timeout 毫秒;如果为零,则无限期地阻塞;必须为非负数
//返回 已更新其准备就绪操作集的键的数目,该数目可能为零
public abstract int select(long timeout) throws IOException;
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
//此方法执行处于阻塞模式的选择操作。仅在至少选择一个通道、调用此选择器的 wakeup 方法,或者当前的线程已中断(以先到者为准)后此方法才返回。
//返回 已更新其准备就绪操作集的键的数目,该数目可能为零
public abstract int select() throws IOException;
//使尚未返回的第一个选择操作立即返回。
//如果另一个线程目前正阻塞在 select() 或 select(long) 方法的调用中,则该调用将立即返回。
// 如果当前未进行选择操作,那么在没有同时调用 selectNow() 方法的情况下,对上述方法的下一次调用将立即返回。
// 在任一情况下,该调用返回的值可能是非零的。如果未同时再次调用此方法,则照常阻塞 select() 或 select(long) 方法的后续调用。
//在两个连续的选择操作之间多次调用此方法与只调用一次的效果相同。
public abstract Selector wakeup();
//关闭此选择器。
// 如果某个线程目前正阻塞在此选择器的某个选择方法中,则中断该线程,如同调用该选择器的 wakeup 方法那样。
// 所有仍与此选择器关联的未取消键已无效、其通道已注销,并且与此选择器关联的所有其他资源已释放。
// 如果此选择器已经关闭,则调用此方法无效。
// 关闭选择器后,除了调用此方法或 wakeup 方法外,以任何其他方式继续使用它都将导致抛出 ClosedSelectorException。
public abstract void close() throws IOException;
}