Java NIO Selector

来源:互联网 发布:苹果电脑激活windows 编辑:程序博客网 时间:2024/06/06 14:15

Java NIO Selector

@(译)[韦文丰]

A Selector is a Java NIO component which can examine one or more NIO Channel’s, and determine which channels are ready for e.g. reading or writing. This way a single thread can manage multiple channels, and thus multiple network connections.

选择器是java NIO一个检测一个或多个NIO Channels的组件,用来检测哪一个channels做好了读写操作等等。通过这种方式,一个线程可以管理多个channels,因此管理多个网络连接。

Why Use a Selector?

为什么使用选择器?

The advantage of using just a single thread to handle multiple channels is that you need less threads to handle the channels. Actually, you can use just one thread to handle all of your channels. Switching between threads is expensive for an operating system, and each thread takes up some resources (memory) in the operating system too. Therefore, the less threads you use, the better.

使用选择器的优点是使用更少的线程来管理channels,事实上,你可以仅仅使用一个线程来管理所有的channels。对操作系统来说,线程间的切换是花费代价大的,并且每个线程要占用操作系统的一些资源(内存)。因此,线程越少越好。

Keep in mind though, that modern operating systems and CPU’s become better and better at multitasking, so the overheads of multithreading becomes smaller over time. In fact, if a CPU has multiple cores, you might be wasting CPU power by not multitasking. Anyways, that design discussion belongs in a different text. It suffices to say here, that you can handle multiple channels with a single thread, using a Selector.

想想看,现代的操作系统和CPU在多任务的情况下表现得越来越好,随着时间的流逝,多线程的开销越来越小。事实上,如果你的CPU是多核的,非多任务的情况下是在浪费CPU的电量。总结下,你可以通过一个线程使用选择器来控制多个channels。

这有一个单线程使用选择器来控制3个channels的图:
这里写图片描述

Creating a Selector

创建一个选择器

You create a Selector by calling the Selector.open() method, like this:

你可以通过调用Selector.open() 来创建一个选择器,像这样:

Selector selector = Selector.open();

Registering Channels with the Selector

给channels注册选择器

In order to use a Channel with a Selector you must register the Channel with the Selector. This is done using the SelectableChannel.register() method, like this:

为了使用带有选择器的Channel,你必须给channel注册选择器 通过使用SelectableChannel.register()来完成,像这样:

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

The Channel must be in non-blocking mode to be used with a Selector. This means that you cannot use FileChannel’s with a Selector since FileChannel’s cannot be switched into non-blocking mode. Socket channels will work fine though.

在选择器的使用中,channel必须配置成非阻塞模式,这意味着你不能通过选择器使用FileChannels,因为FileChannels不能转换成非阻塞模式。不过Socket Channels可以很好地工作。

Notice the second parameter of the register() method. This is an “interest set”, meaning what events you are interested in listening for in the Channel, via the Selector. There are four different events you can listen for:

注意register()方法的第二个参数,这是“兴趣集”,也就是你通过选择器在Channel中监听的感兴趣事件。有四个你可以监听的事件:

Connect
Accept
Read
Write

1.连接
2.接受请求
3.读
4.写

A channel that “fires an event” is also said to be “ready” for that event. So, a channel that has connected successfully to another server is “connect ready”. A server socket channel which accepts an incoming connection is “accept” ready. A channel that has data ready to be read is “read” ready. A channel that is ready for you to write data to it, is “write” ready.

一个”fires an event”的channel也就是为事件做好了准备。因此,”connect” ready是已经成功连接到别的服务器,”accept” ready是接受到了连接的请求,”read” ready是已经读取到了数据,”whrite” ready是准备好了写操作。

These four events are represented by the four SelectionKey constants:

这四种事件是SelectionKey 常量的代表:

SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

If you are interested in more than one event, OR the constants together, like this:
如果你对不只一个事件感兴趣,可以把常量组织在一块,像这样:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

I’ll return to the interest set a bit further down in this text.
接下来我将更多地介绍interest set

SelectionKey’s

SelectionKey’s

As you saw in the previous section, when you register a Channel with a Selector the register() method returns a SelectionKey objects. This SelectionKey object contains a few interesting properties:

The interest set
The ready set
The Channel
The Selector
An attached object (optional)
I’ll describe these properties below.

正如你在之前的章节看到的,当你通过选择器的register()方法注册channel后返回一个SelectionKey。这个SelectionKey包含了一些有意思的属性:

The interest set
The ready set
The Channel
The Selector
An attached object (optional)
接下来我将描述介绍这些属性。

Interest Set

Interest Set

The interest set is the set of events you are interested in “selecting”, as described in the section “Registering Channels with the Selector”. You can read and write that interest set via the SelectionKey like this:

兴趣集是一系列你选择的感兴趣事件,正如在“给channels注册选择器”章节描述的一样。你可以通过SelectionKey来读写兴趣集,像这样:

int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

As you can see, you can AND the interest set with the given SelectionKey constant to find out if a certain event is in the interest set.

就像你看到的一样,你可以通过 AND 兴趣集和给定的SelectionKey常量来找出一个确定的事件是否在兴趣集中。

Ready Set

Ready Set

The ready set is the set of operations the channel is ready for. You will primarily be accessing the ready set after a selection. Selection is explained in a later section. You access the ready set like this:

int readySet = selectionKey.readyOps();

准备集是一系列channel准备好的操作。在selection之后你最初访问的是准备集。Selection在随后的章节中介绍。你可以这样访问准备集:

int readySet = selectionKey.readyOps();

You can test in the same way as with the interest set, what events / operations the channel is ready for. But, you can also use these four methods instead, which all reaturn a boolean:

你可以通过像测试兴趣集类似的方式一样来测试什么事件/操作channel是准备好了的。但是,你也可以使用这四种方法代替,方法返回boolean:

selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();

Channel + Selector

Channel + Selector

Accessing the channel + selector from the SelectionKey is trivial. Here is how it’s done:

从SelectionKey中获取channel+selector很简单:

Channel channel = selectionKey.channel();
Selector selector = selectionKey.selector();

Attaching Objects

绑定对象

You can attach an object to a SelectionKey this is a handy way of recognizing a given channel, or attaching further information to the channel. For instance, you may attach the Buffer you are using with the channel, or an object containing more aggregate data. Here is how you attach objects:

你可以给SelectionKey绑定一个对象,这是一种轻松辨别出给定channel的方式,或者也可以给channel绑定更多的信息。例如,你可以绑定一个和channel一块用的Buffer,或者一个包含更多集合数据的对象。例子:

selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();

You can also attach an object already while registering the Channel with the Selector, in the register() method. Here is how that looks:

你也可以在给选择器注册channel时在register()方法中绑定一个对象。这样:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

Selecting Channels via a Selector

通过选择器选择channel

Once you have register one or more channels with a Selector you can call one of the select() methods. These methods return the channels that are “ready” for the events you are interested in (connect, accept, read or write). In other words, if you are interested in channels that are ready for reading, you will receive the channels that are ready for reading from the select() methods.

一旦你给Selector注册一个或多个channels,你可以调用select()中的一个方法。这些方法返回为感兴趣的事件(连接,接受,读,写)准备好的channel。换句话说,如果你感情的channel准备好读,你会得到从select()返回的channel。

Here are the select() methods:
一些select()方法:

int select()
int select(long timeout)
int selectNow()

select() blocks until at least one channel is ready for the events you registered for.
select()方法阻塞直到至少一个你注册的事件准备好

select(long timeout) does the same as select() except it blocks for a maximum of timeout milliseconds (the parameter).
select(long timeout)和 select()一样除了它阻塞一定的延时时间

selectNow() doesn’t block at all. It returns immediately with whatever channels are ready.
selectNow()不阻塞,不管channel有没有准备好它都马上返回

The int returned by the select() methods tells how many channels are ready. That is, how many channels that became ready since last time you called select(). If you call select() and it returns 1 because one channel has become ready, and you call select() one more time, and one more channel has become ready, it will return 1 again. If you have done nothing with the first channel that was ready, you now have 2 ready channels, but only one channel had become ready between each select() call.

select()回的int告诉我们有多少个channel准备好了。也就从上次调用select()起,有多少channel准备好了。如果你调用select()并且它返回1因为一个channel已经准备好了,当你不止一次调用select()并且不止一个channel已经准备好,它会再次返回1。如果你对第一个准备好的channel不做任何处理,现在就有两个channel,但是在每次select()调用之间只有一个channel准备好。

selectedKeys()

selectedKeys()

Once you have called one of the select() methods and its return value has indicated that one or more channels are ready, you can access the ready channels via the “selected key set”, by calling the selectors selectedKeys() method. Here is how that looks:

一旦你调用一种select()方法并且它的返回值表明一个或多个channel已经准备好,你可以通过”selected key set”来获得准备好的channnel,通过调用选择器的selectedKeys()方法,像这样:

Set selectedKeys = selector.selectedKeys();

When you register a channel with a Selector the Channel.register() method returns a SelectionKey object. This key represents that channels registration with that selector. It is these keys you can access via the selectedKeySet() method. From the SelectionKey.

当你给选择器注册一个channel,Channel.register()方法返回一个SelectionKey对象。这个key表明选择器注册到了channel上。你可以铜过selectedKeySet()获取到这些keys。

You can iterate this selected key set to access the ready channels. Here is how that looks:

你可以遍历这些 selected key set来获取准备好的channel,像这样:

Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {    SelectionKey key = keyIterator.next();    if(key.isAcceptable()) {        // a connection was accepted by a ServerSocketChannel.    } else if (key.isConnectable()) {        // a connection was established with a remote server.    } else if (key.isReadable()) {        // a channel is ready for reading    } else if (key.isWritable()) {        // a channel is ready for writing    }    keyIterator.remove();}

This loop iterates the keys in the selected key set. For each key it tests the key to determine what the channel referenced by the key is ready for.

这个循环遍历the selected key set中的keys,它检测每一个key来判断哪一个被key引用的channel已经准备好。

Notice the keyIterator.remove() call at the end of each iteration. The Selector does not remove the SelectionKey instances from the selected key set itself. You have to do this, when you are done processing the channel. The next time the channel becomes “ready” the Selector will add it to the selected key set again.

注意在遍历最后的keyIterator.remove()方法。选择器不会从selected key set 移除SelectionKey的实例。当你对channel做处理后你需要从selected key set 移除SelectionKey的实例。下次当channel准备好选择器会再次把它加入selected key set中。

The channel returned by the SelectionKey.channel() method should be cast to the channel you need to work with, e.g a ServerSocketChannel or SocketChannel etc.

SelectionKey.channel()返回的channel应该转型为你需要的channel类型,如ServerSocketChannel或者SocketChannel等等。

wakeUp()

wakeUp()

A thread that has called the select() method which is blocked, can be made to leave the select() method, even if no channels are yet ready. This is done by having a different thread call the Selector.wakeup() method on the Selector which the first thread has called select() on. The thread waiting inside select() will then return immediately.

一个曾经调用select()方法被阻塞的线程可以被强迫离开select()方法,即使没有channel准备好。这通过它调用select()方法所在选择器的另一个不同的线程调用Selector.wakeup()来完成。在select()方法中等待的线程会马上返回。

If a different thread calls wakeup() and no thread is currently blocked inside select(), the next thread that calls select() will “wake up” immediately.

如果一个不同的线程调用wakeup()方法并且目前没有线程被阻塞,下一个调用select()方法的线程会马上被唤醒。

close()

close()

When you are finished with the Selector you call its close() method. This closes the Selector and invalidates all SelectionKey instances registered with this Selector. The channels themselves are not closed.

当你结束选择器时调用它的close()方法。这会关闭选择器并使所有注册了该选择器的SelectionKey 实例无效。channels本身不会关闭。

Full Selector Example

Selector完整例子

Here is a full example which opens a Selector, registers a channel with it (the channel instantiation is left out), and keeps monitoring the Selector for “readiness” of the four events (accept, connect, read, write).

这是一个打开选择器,在channel上注册它(channel初始化没被包括进这段代码),监控Selector四个准备好的事件(接受,连接,读,写)。

Selector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectionKey.OP_READ);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()) {        // a connection was accepted by a ServerSocketChannel.    } else if (key.isConnectable()) {        // a connection was established with a remote server.    } else if (key.isReadable()) {        // a channel is ready for reading    } else if (key.isWritable()) {        // a channel is ready for writing    }    keyIterator.remove();  }}

原文链接:Java NIO Selector

原创粉丝点击