Reprint: Concurrent Programming Network: ifeve.com NIO Tutorial
Selector (selector) is a Java NIO that can detect one or more NIO channels and know whether the channel is ready for read and write events such as Prepared components. In this way, a single thread can manage multiple channels and thus multiple network connections.
The advantage of using only a single thread to process multiple Channels is that fewer threads are needed to process the channels. In fact, it is possible to use only one thread to handle all channels. For the operating system, context switching between threads is very expensive, and each thread occupies some system resources (such as memory). Therefore, the fewer threads used, the better.
However, you need to remember that modern operating systems and CPUs are getting better and better at multitasking, so the overhead of multithreading becomes smaller and smaller over time. In fact, if a CPU has multiple cores, not using multitasking may be a waste of CPU power. Anyway, the discussion of that design should be in a different article. Here, it is enough to know that you can handle multiple channels using Selector.
Create a Selector by calling the Selector.open() method, as follows:
Selector selector = Selector.open();
In order to use Channel and Selector together, the channel must be registered with the selector. This is achieved through the SelectableChannel.register() method, as follows:
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
When used with Selector, Channel must be in non-blocking mode. This means that you cannot use FileChannel with a Selector because FileChannel cannot be switched to non-blocking mode. Socket channels are fine.
Pay attention to the second parameter of the register() method. This is an "interest collection", which means what events you are interested in when listening to the Channel through the Selector. Four different types of events can be listened to:
Connect
Accept
Read
Write
The channel triggers an event, which means that the event is ready. Therefore, a channel that successfully connects to another server is called "connection ready". A server socket channel is said to be "ready to receive" when it is ready to receive incoming connections. A channel that has data to read is said to be "read-ready". A channel waiting to write data can be said to be "write-ready".
These four events are represented by the four constants of SelectionKey:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
If you are interested in more than one event , then you can use the "bit OR" operator to connect the constants, as follows:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
The interest collection will be mentioned below.
In the previous section, when registering a Channel with the Selector, the register() method will return a SelectionKey object. This object contains some properties that interest you:
interest collection
ready collection
Channel
Selector
Additional object (optional)
I will describe it below these properties.
As described in the section Registering a Channel with a Selector, the interest collection is a collection of interesting events that you select. You can read and write the interest collection through SelectionKey, like this:
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
You can see that using "bit AND" to operate the interest collection and the given SelectionKey constant can determine whether a certain event is in the interest collection.
ready collection is a collection of operations for which the channel is ready. After a selection (Selection), you will first access the ready set. Selection will be explained in the next section. You can access the ready collection like this:
int readySet = selectionKey.readyOps();
You can use the same method as detecting the interest collection to detect which events or operations in the channel are ready. However, the following four methods are also available, all of which return a Boolean type:
selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();
Accessing Channel and Selector from SelectionKey is simple. As follows:
Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();
You can attach an object or more information to the SelectionKey, so that a given channel can be easily identified. For example, you can attach a Buffer to be used with a channel, or an object containing aggregated data. The usage method is as follows:
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();
You can also attach objects when registering the Channel with the Selector using the register() method. Such as:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
Once one or more channels are registered with Selector, you can call several overloaded select() methods. These methods return those channels that are ready for the event you are interested in (such as connect, accept, read, or write). In other words, if you are interested in "read-ready" channels, the select() method will return those channels for which read events are ready.
The following is the select() method:
int select()
int select(long timeout)
int selectNow()
select()
阻塞到至少有一个通道在你注册的事件上就绪了。
select(long timeout)
和select()一样,除了最长会阻塞timeout毫秒(参数)。
selectNow()
不会阻塞,不管什么通道就绪都立刻返回(译者注:此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。)。
select()方法返回的int值表示有多少通道已经就绪。亦即,自上次调用select()方法后有多少通道变成就绪状态。如果调用select()方法,因为有一个通道变成就绪状态,返回了1,若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。
一旦调用了select()方法,并且返回值表明有一个或更多个通道就绪了,然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key set)”中的就绪通道。如下所示:
Set selectedKeys = selector.selectedKeys();
当像Selector注册Channel时,Channel.register()方法会返回一个SelectionKey 对象。这个对象代表了注册到该Selector的通道。可以通过SelectionKey的selectedKeySet()方法访问这些对象。
/** * selector */@Testpublic void test3() throws IOException { Selector selector = Selector.open(); SocketChannel socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false);//向selector注册此通道SelectionKey register = socketChannel.register(selector, SelectionKey.OP_READ);//在本例,i == SelectionKey.OP_READint i = register.interestOps();//所以判断 可以通过这样来判断事件boolean b = (i & SelectionKey.OP_READ) == SelectionKey.OP_READ;//当然也可以通过 isXX方法来判断boolean readable = register.isReadable();//返回已经准备好的 SelectionKey数量,如果>0,表示有了,就可以调用下面的方法了int select = selector.select();/** * if select > 0,一般是 while(true)循环 *///这里面保存着已经 准备好的 SelectionKey,也就是通道//注意 这里面的 SelectionKey需要手动移除,不会自动移除Set7242e6f37ad3976ef4c41538eb6faedf selectionKeys = selector.selectedKeys(); Iterator keyIterator = selectionKeys.iterator();while(keyIterator.hasNext()) { SelectionKey key = (SelectionKey) keyIterator.next();//获取通道SelectableChannel channel = key.channel();//获取selectorSelector selector1 = key.selector();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(); }//关闭 selector.close(); }
注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。
SelectionKey.channel()方法返回的通道需要转型成你要处理的类型,如ServerSocketChannel或SocketChannel等。
某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。阻塞在select()方法上的线程会立马返回。
如果有其它线程调用了wakeup()方法,但当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立即“醒来(wake up)”。
用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。
The above is the detailed content of Selector of JAVA-5NIO. For more information, please follow other related articles on the PHP Chinese website!