2009-06-29 11 views
13

Tengo una pregunta básica. Por qué y cómo el método de registro de SelectableChannel puede estar en la llamada de bloqueo. Déjame proporcionar un escenario.El hilo de Java bloquea al registrar el canal con el selector mientras se llama a select(). ¿Qué hacer?

He creado un objeto selector en la clase Regístrese de la siguiente manera.

private static Selector selector = Selector.open(); 

También tengo un método en la misma clase (Registrarse) para registrar el canal con el selector.

public static SelectionKey registerChannel(SelectableChannel channel, 
      int ops) throws IOException { 

     channel.configureBlocking(false); 
     return channel.register(selector, ops); 
    } 

Y hay otra clase llamada de solicitud, el cual tiene un método que lee los datos de los canales, los procesos y las llamadas siguientes método para registrar el canal.

selectonKey = Register.register(socketChannel, SelectionKey.OP_READ); 

Aquí en este punto el hilo está bloqueado, sin dar la pista de lo que está esperando. He verificado que el selector está abierto. Por favor, dame algo de ayuda para entender cómo puedo resolver esto. ¿Hay algún bloqueo que pueda liberar?

Cualquier entrada sería apreciada.

Agregando a lo que describí. Pruebas adicionales revelaron que si el método Register.register se llama desde el mismo hilo, puede registrarse, pero luego si otro hilo intenta invocar el método, thread doesn, t move ahead.

Respuesta

0

¿Ha intentado imprimir un seguimiento de la pila de todos los hilos en su programa (usando kill -QUIT en Unix o Ctrl + Break en Windows o usando la utilidad jstack)?

AbstractSelectableChannel contiene un bloqueo en el que configureBlocking y register deben sincronizarse. También se puede acceder a este bloqueo a través del método blockingLock(), por lo que otro subproceso podría contener el bloqueo, lo que provocaría que su llamada de registro se bloquee indefinidamente (pero sin un seguimiento de pila es difícil de detectar).

7

Debe usar un candado y sincronizar manualmente.

En el mismo hilo está ejecutando el bucle de selección tienen una ReentrantLock:

final ReentrantLock selectorLock = new ReentrantLock(); 

Luego, cuando es necesario registrarse con el selector de hacer algo como esto:

selectorLock.lock(); 
try { 
    selector.wakeup(); 
    socketChannel.register(selector, ops); 
} finally { 
    selectorLock.unlock(); 
} 

Por último, durante su loop que está llamando accept(), algo como esto:

selectorLock.lock(); 
selectorLock.unlock(); 

selector.select(500); 

yt Seguirá con el resto de su lógica.

Esta construcción garantiza que la llamada register() no bloqueará al asegurar que nunca otra select() es entre correspondiente wakeup()register() y llamadas.

+0

+1 para el ejemplo de código. En segundo lugar esto, por experiencia. Bien hecho. – casey

+0

Esto agrega hasta 500 ms de latencia innecesaria a cada registro. Use el enfoque en http://stackoverflow.com/a/2179612/448970 en su lugar. –

+0

@DavidB. No agrega ninguna latencia a cada registro. Es el hilo de selección que bloquea hasta 500 ms, no el hilo de registro, y el hilo de selección siempre querrá bloquear en 'select()' de todos modos. Las afirmaciones en la respuesta son en su mayoría incorrectas. – EJP

28

Esta es una característica básica de la mayoría de las implementaciones de NIO que no es obvia en la documentación.

Usted necesita para hacer todas las llamadas del registro de la misma rosca que está haciendo su selección o callejones sin salida va a producir. Por lo general, esto se hace proporcionando una cola de registros/desregistros/cambios de interés en los que se escribe y luego se llama a selector.wakeup(). Cuando se despierta el hilo de selección, comprueba la cola y realiza las operaciones solicitadas.

+1

Esta es una característica básica de * todas * las implementaciones de NIO que está completamente especificada en el Javadoc. Hay tres sincronizaciones anidadas mientras está en 'select(),' y 'register()' intenta una de ellas. El resultado no es un interbloqueo sino un * bloque *, que solo dura hasta que devuelve la llamada concurrente 'select()'. La solución es llamar 'despertar()' antes del 'registro().' Eso obliga a la 'Select()' para desbloquear y volver a cero, lo que libera sus tres cerraduras, lo que permite 'registro()' para reclamar el uno que necesita y proceder. – EJP

+2

La solución proporcionada en el comentario anterior es incorrecta, al menos, dada la explicación. Dependiendo de la programación de los hilos, el hilo de selección puede completar un ciclo completo y volver a ingresar a select() antes de que el hilo de registro pueda llamar a register(). – dsd

0

Registra tu canal desde cualquier tema:

synchronized (selectorLock2) { 
    selector.wakeup(); 
    synchronized (selectorLock1) { 
     channel.register(selector, ops); 
    } 
} 

su bucle de selección debe tener este aspecto:

while (true) { 
    synchronized (selectorLock1) { 
     selector.select(); 
    } 
    synchronized (selectorLock2) {} 

    .... 
} 
+0

Solo necesita un solo candado como se muestra en https://stackoverflow.com/a/1112809/194894 – Flow

Cuestiones relacionadas