Nachdruck: Concurrent Programming Network: ifeve.com NIO Tutorial
Selektor (Selektor) ist ein Java-NIO, der einen oder mehrere NIO-Kanäle erkennen und wissen kann, ob der Kanal für Lese- und Schreibereignisse vorbereitet ist Komponenten. Auf diese Weise kann ein einzelner Thread mehrere Kanäle und damit mehrere Netzwerkverbindungen verwalten.
Der Vorteil der Verwendung nur eines einzigen Threads zur Verarbeitung mehrerer Kanäle besteht darin, dass weniger Threads zur Verarbeitung der Kanäle erforderlich sind. Tatsächlich ist es möglich, nur einen Thread zur Verarbeitung aller Kanäle zu verwenden. Für das Betriebssystem ist der Kontextwechsel zwischen Threads sehr kostspielig und jeder Thread belegt einige Systemressourcen (z. B. Speicher). Daher gilt: Je weniger Threads verwendet werden, desto besser.
Es ist jedoch wichtig zu bedenken, dass moderne Betriebssysteme und CPUs beim Multitasking immer besser werden, sodass der Overhead von Multithreading mit der Zeit immer kleiner wird. Wenn eine CPU über mehrere Kerne verfügt, kann der Verzicht auf Multitasking sogar eine Verschwendung von CPU-Leistung darstellen. Wie auch immer, die Diskussion dieses Designs sollte in einem anderen Artikel erfolgen. Hier reicht es zu wissen, dass Sie mit Selector mehrere Kanäle verwalten können.
Erstellen Sie einen Selektor, indem Sie die Selector.open()-Methode wie folgt aufrufen:
Selector selector = Selector.open();
Um Kanal und Selektor zusammen zu verwenden, muss der Kanal beim Selektor registriert werden. Dies wird durch die SelectableChannel.register()-Methode wie folgt erreicht:
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Bei Verwendung mit Selector muss sich der Kanal im nicht blockierenden Modus befinden. Das bedeutet, dass Sie FileChannel nicht mit einem Selector verwenden können, da FileChannel nicht in den nicht blockierenden Modus geschaltet werden kann. Socket-Kanäle sind in Ordnung.
Achten Sie auf den zweiten Parameter der Methode register(). Dabei handelt es sich um eine „Interessensammlung“, also die Ereignisse, die Sie interessieren, wenn Sie den Kanal über den Selector anhören. Es können vier verschiedene Arten von Ereignissen angehört werden:
Verbinden
Akzeptieren
Lesen
Schreiben
Der Kanal löst ein Ereignis aus, was bedeutet, dass das Ereignis bereit ist. Daher wird ein Kanal, der erfolgreich eine Verbindung zu einem anderen Server herstellt, als „verbindungsbereit“ bezeichnet. Ein Server-Socket-Kanal gilt als „empfangsbereit“, wenn er bereit ist, eingehende Verbindungen zu empfangen. Ein Kanal, der Daten zum Lesen hat, wird als „lesebereit“ bezeichnet. Ein Kanal, der darauf wartet, Daten zu schreiben, kann als „schreibbereit“ bezeichnet werden.
Diese vier Ereignisse werden durch die vier Konstanten von SelectionKey dargestellt:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
Wenn Sie an mehr als einer Veranstaltung interessiert sind, Dann können Sie den Operator „bitweises ODER“ verwenden, um die Konstanten wie folgt zu verbinden:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
Die Zinssammlung wird unten erwähnt.
Im vorherigen Abschnitt gibt die Methode register() bei der Registrierung eines Kanals beim Selector ein SelectionKey-Objekt zurück. Dieses Objekt enthält einige Eigenschaften, die Sie interessieren:
interessante Sammlung
bereite Sammlung
Kanal
Selektor
Zusätzliches Objekt (optional)
Ich werde es unterhalb dieser Eigenschaften beschreiben.
Wie im Abschnitt Registrieren von Kanälen mit einem Selektor beschrieben, ist die Interessensammlung eine Sammlung interessanter Ereignisse, die Sie auswählen. Sie können die Zinssammlung über SelectionKey lesen und schreiben, wie folgt:
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
Wie Sie sehen können, kann durch die Verwendung von „Bit AND“ zum Betreiben der Zinssammlung und der angegebenen SelectionKey-Konstante bestimmt werden, ob ein bestimmtes Ereignis vorliegt in der Zinssammlung Mitte.
Bereitschaftssammlung ist eine Sammlung von Vorgängen, für die der Kanal bereit ist. Nach einer Auswahl (Auswahl) gelangen Sie zunächst zum fertigen Set. Die Auswahl wird im nächsten Abschnitt erläutert. Sie können wie folgt auf die Bereitschaftssammlung zugreifen:
int readySet = selectionKey.readyOps();
Sie können dieselbe Methode wie die Interessensammlung verwenden, um zu erkennen, welche Ereignisse oder Vorgänge im Kanal bereit sind. Es stehen jedoch auch die folgenden vier Methoden zur Verfügung, die alle einen booleschen Typ zurückgeben:
selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();
Der Zugriff auf Channel und Selector über SelectionKey ist einfach. Wie folgt:
Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();
können ein Objekt oder weitere Informationen an den SelectionKey anhängen, sodass ein bestimmter Kanal leicht identifiziert werden kann. Sie können beispielsweise einen Puffer anhängen, der mit einem Kanal verwendet werden soll, oder ein Objekt, das aggregierte Daten enthält. Die Verwendungsmethode ist wie folgt:
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();
Sie können auch Objekte anhängen, wenn Sie den Kanal beim Selector mithilfe der Methode register() registrieren. Zum Beispiel:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
Sobald ein oder mehrere Kanäle bei Selector registriert sind, können Sie mehrere überladene select()-Methoden aufrufen. Diese Methoden geben die Kanäle zurück, die für das Ereignis bereit sind, an dem Sie interessiert sind (z. B. Verbinden, Akzeptieren, Lesen oder Schreiben). Mit anderen Worten: Wenn Sie an „lesebereiten“ Kanälen interessiert sind, gibt die Methode select() die Kanäle zurück, für die Leseereignisse bereit sind.
Das Folgende ist die Methode select():
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实例无效。通道本身并不会关闭。
Das obige ist der detaillierte Inhalt vonSelektor von JAVA-5NIO. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!