>  기사  >  Java  >  JAVA-5NIO 선택기

JAVA-5NIO 선택기

巴扎黑
巴扎黑원래의
2017-06-26 09:30:541126검색

재인쇄: 동시 프로그래밍 네트워크: ifeve.com NIO 튜토리얼

Selector(선택기)는 하나 이상의 NIO 채널을 감지하고 해당 채널이 읽기 및 쓰기와 같은 이벤트에 준비되어 있는지 여부를 알 수 있는 Java NIO의 구성 요소입니다. 이러한 방식으로 단일 스레드는 여러 채널을 관리하여 여러 네트워크 연결을 관리할 수 있습니다.

1. Selector를 사용하는 이유

단일 스레드만 사용하여 여러 채널을 처리할 때의 장점은 채널을 처리하는 데 필요한 스레드 수가 적다는 것입니다. 실제로 하나의 스레드만 사용하여 모든 채널을 처리하는 것이 가능합니다. 운영 체제의 경우 스레드 간 컨텍스트 전환은 매우 비용이 많이 들고 각 스레드는 일부 시스템 리소스(예: 메모리)를 차지합니다. 따라서 사용되는 스레드 수가 적을수록 좋습니다.

그러나 최신 운영 체제와 CPU는 멀티태스킹 능력이 점점 더 좋아지고 있으므로 멀티스레딩의 오버헤드는 시간이 지남에 따라 점점 작아진다는 점을 기억해야 합니다. 실제로 CPU에 다중 코어가 있는 경우 멀티태스킹을 사용하지 않는 것은 CPU 성능을 낭비하는 것일 수 있습니다. 어쨌든, 그 디자인에 대한 논의는 다른 글에서 다루어야 할 것 같습니다. 여기서는 Selector를 사용하여 여러 채널을 처리할 수 있다는 점만 알아도 충분합니다.

2. Selector 생성

다음과 같이 Selector.open() 메서드를 호출하여 Selector를 생성합니다.

Selector selector = Selector.open();

3. Channel과 Selector를 함께 사용하려면 채널을 등록해야 합니다. 선택기에 등록됩니다. 이는 다음과 같이 SelectableChannel.register() 메서드를 통해 수행됩니다.

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

Selector와 함께 사용할 경우 채널은 비차단 모드에 있어야 합니다. 이는 FileChannel을 비차단 모드로 전환할 수 없기 때문에 선택기와 함께 FileChannel을 사용할 수 없음을 의미합니다. 소켓 채널은 괜찮습니다.

register() 메소드의 두 번째 매개변수에 주목하세요. 이는 "관심 컬렉션"으로, 선택기를 통해 채널을 청취할 때 어떤 이벤트에 관심이 있는지를 의미합니다. 네 가지 유형의 이벤트를 들을 수 있습니다.

    Connect
  1. Accept
  2. Read
  3. Write
  4. 채널은 이벤트가 준비되었음을 의미하는 이벤트를 트리거합니다. 따라서 다른 서버에 성공적으로 연결한 채널을 "연결 준비"라고 합니다. 서버 소켓 채널은 들어오는 연결을 받을 준비가 되었을 때 "수신 준비" 상태라고 합니다. 읽을 데이터가 있는 채널을 "읽기 준비" 상태라고 합니다. 데이터 쓰기를 대기 중인 채널은 "쓰기 준비" 상태라고 할 수 있습니다.

이 네 가지 이벤트는 SelectionKey의 네 가지 상수로 표시됩니다.

    SelectionKey.OP_CONNECT
  1. SelectionKey.OP_ACCEPT
  2. SelectionKey.OP_READ
  3. SelectionKey.OP_WRITE
  4. 당신이 옳다면 하나 이상의 이벤트에 관심이 있는 경우 "bit-OR" 연산자를 사용하여 다음과 같이 상수를 연결할 수 있습니다.
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

관심도 수집에 대해서는 아래에서 언급하겠습니다.

4. SelectionKey

이전 섹션에서 Selector에 채널을 등록할 때 Register() 메서드는 SelectionKey 개체를 반환합니다. 이 개체에는 관심을 가질 만한 몇 가지 속성이 포함되어 있습니다.

    ready 컬렉션
  • Channel
  • Selector
  • 추가 개체(선택 사항)
  • 내 아래에 있는 속성은 다음과 같습니다. 설명했습니다.
  • 관심 컬렉션

  • 선택기로 채널 등록 섹션에 설명된 대로 관심 컬렉션은 사용자가 선택한 흥미로운 이벤트의 모음입니다. 다음과 같이 SelectionKey를 통해 관심 컬렉션을 읽고 쓸 수 있습니다.
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept  = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;

"비트 AND"를 사용하여 관심 컬렉션을 작동하고 주어진 SelectionKey 상수에 따라 특정 이벤트가 관심 컬렉션에 있는지 여부를 확인할 수 있음을 알 수 있습니다.

ready collection

ready collection은 채널이 준비된 작업 모음입니다. 선택(Selection) 후에는 먼저 준비된 세트에 접근하게 됩니다. 선택에 대해서는 다음 섹션에서 설명하겠습니다. 다음과 같이 준비된 컬렉션에 액세스할 수 있습니다.

int readySet = selectionKey.readyOps();

관심 컬렉션을 감지하는 것과 동일한 방법을 사용하여 채널에서 준비된 이벤트나 작업을 감지할 수 있습니다. 그러나 다음 네 가지 방법도 사용할 수 있으며 모두 부울 유형을 반환합니다.

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

Channel + Selector

SelectionKey에서 채널 및 선택기에 액세스하는 것은 간단합니다. 다음과 같습니다:

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

첨부된 개체

SelectionKey에 개체나 추가 정보를 첨부하여 해당 채널을 쉽게 식별할 수 있습니다. 예를 들어 채널과 함께 사용할 버퍼나 집계된 데이터가 포함된 개체를 연결할 수 있습니다. 사용법은 다음과 같습니다.

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

register() 메소드를 사용하여 Selector에 채널을 등록할 때 객체를 첨부할 수도 있습니다. 예:

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

5. Selector를 통해 채널 선택

Selector에 하나 이상의 채널이 등록되면 오버로드된 여러 select() 메서드를 호출할 수 있습니다. 이러한 메서드는 관심 있는 이벤트(예: 연결, 수락, 읽기 또는 쓰기)에 대해 준비된 채널을 반환합니다. 즉, "읽기 준비된" 채널에 관심이 있는 경우 select() 메서드는 읽기 이벤트가 준비된 채널을 반환합니다.

다음은 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()方法调用之间,只有一个通道就绪了。

selectedKeys()

一旦调用了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等。

wakeUp()

某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()方法即可。阻塞在select()方法上的线程会立马返回。

如果有其它线程调用了wakeup()方法,但当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立即“醒来(wake up)”。

close()

用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。

 

 

위 내용은 JAVA-5NIO 선택기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.