재인쇄: 동시 프로그래밍 네트워크: ifeve.com NIO 튜토리얼
Selector(선택기)는 하나 이상의 NIO 채널을 감지하고 해당 채널이 읽기 및 쓰기와 같은 이벤트에 준비되어 있는지 여부를 알 수 있는 Java NIO의 구성 요소입니다. 이러한 방식으로 단일 스레드는 여러 채널을 관리하여 여러 네트워크 연결을 관리할 수 있습니다.
단일 스레드만 사용하여 여러 채널을 처리할 때의 장점은 채널을 처리하는 데 필요한 스레드 수가 적다는 것입니다. 실제로 하나의 스레드만 사용하여 모든 채널을 처리하는 것이 가능합니다. 운영 체제의 경우 스레드 간 컨텍스트 전환은 매우 비용이 많이 들고 각 스레드는 일부 시스템 리소스(예: 메모리)를 차지합니다. 따라서 사용되는 스레드 수가 적을수록 좋습니다.
그러나 최신 운영 체제와 CPU는 멀티태스킹 능력이 점점 더 좋아지고 있으므로 멀티스레딩의 오버헤드는 시간이 지남에 따라 점점 작아진다는 점을 기억해야 합니다. 실제로 CPU에 다중 코어가 있는 경우 멀티태스킹을 사용하지 않는 것은 CPU 성능을 낭비하는 것일 수 있습니다. 어쨌든, 그 디자인에 대한 논의는 다른 글에서 다루어야 할 것 같습니다. 여기서는 Selector를 사용하여 여러 채널을 처리할 수 있다는 점만 알아도 충분합니다.
다음과 같이 Selector.open() 메서드를 호출하여 Selector를 생성합니다.
Selector selector = Selector.open();
channel.configureBlocking(false); SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Selector와 함께 사용할 경우 채널은 비차단 모드에 있어야 합니다. 이는 FileChannel을 비차단 모드로 전환할 수 없기 때문에 선택기와 함께 FileChannel을 사용할 수 없음을 의미합니다. 소켓 채널은 괜찮습니다.
register() 메소드의 두 번째 매개변수에 주목하세요. 이는 "관심 컬렉션"으로, 선택기를 통해 채널을 청취할 때 어떤 이벤트에 관심이 있는지를 의미합니다. 네 가지 유형의 이벤트를 들을 수 있습니다.
이 네 가지 이벤트는 SelectionKey의 네 가지 상수로 표시됩니다.
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
관심도 수집에 대해서는 아래에서 언급하겠습니다.
4. SelectionKey
관심 컬렉션
int interestSet = selectionKey.interestOps(); boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
"비트 AND"를 사용하여 관심 컬렉션을 작동하고 주어진 SelectionKey 상수에 따라 특정 이벤트가 관심 컬렉션에 있는지 여부를 확인할 수 있음을 알 수 있습니다.
ready collection은 채널이 준비된 작업 모음입니다. 선택(Selection) 후에는 먼저 준비된 세트에 접근하게 됩니다. 선택에 대해서는 다음 섹션에서 설명하겠습니다. 다음과 같이 준비된 컬렉션에 액세스할 수 있습니다.
int readySet = selectionKey.readyOps();
관심 컬렉션을 감지하는 것과 동일한 방법을 사용하여 채널에서 준비된 이벤트나 작업을 감지할 수 있습니다. 그러나 다음 네 가지 방법도 사용할 수 있으며 모두 부울 유형을 반환합니다.
selectionKey.isAcceptable(); selectionKey.isConnectable(); selectionKey.isReadable(); selectionKey.isWritable();
SelectionKey에서 채널 및 선택기에 액세스하는 것은 간단합니다. 다음과 같습니다:
Channel channel = selectionKey.channel(); Selector selector = selectionKey.selector();
첨부된 개체
selectionKey.attach(theObject); Object attachedObj = selectionKey.attachment();
register() 메소드를 사용하여 Selector에 채널을 등록할 때 객체를 첨부할 수도 있습니다. 예:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
Selector에 하나 이상의 채널이 등록되면 오버로드된 여러 select() 메서드를 호출할 수 있습니다. 이러한 메서드는 관심 있는 이벤트(예: 연결, 수락, 읽기 또는 쓰기)에 대해 준비된 채널을 반환합니다. 즉, "읽기 준비된" 채널에 관심이 있는 경우 select() 메서드는 읽기 이벤트가 준비된 채널을 반환합니다.
다음은 select() 메소드입니다:
int select()
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实例无效。通道本身并不会关闭。
위 내용은 JAVA-5NIO 선택기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!