2009-06-29 1 views
13

Ho una domanda di base. Perché e come il metodo di registrazione di SelectableChannel può essere nella chiamata di blocco. Consentitemi di fornire uno scenario.Blocco thread Java durante la registrazione del canale con selettore mentre viene chiamato select(). Cosa fare?

Ho creato un oggetto Selector nel registro di classe come segue.

private static Selector selector = Selector.open(); 

Ho anche un metodo nella stessa classe (Registro) per registrare il canale con il selettore.

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

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

E c'è un'altra classe di nome request, che ha metodo che legge i dati provenienti da canali, i processi e le chiamate seguente metodo per registrare il canale.

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

Qui a questo punto il thread è bloccato, non dando la chiave di ciò che sta aspettando. Ho verificato che il selettore è aperto. Per favore forniscimi un aiuto per capire come posso risolvere questo problema. C'è un blocco che posso rilasciare.

Qualsiasi input sarebbe apprezzato.

In aggiunta a ciò che ho descritto. Ulteriori test hanno rivelato che se il metodo Register.register viene chiamato dallo stesso thread, è in grado di registrarsi ma dopo di ciò se qualche altro thread tenta di richiamare il metodo, thread non si muove avanti.

risposta

0

Hai provato a stampare una traccia di stack di tutti i thread nel programma (utilizzando kill -QUIT in Unix o Ctrl + Break in Windows o utilizzando l'utilità jstack)?

AbstractSelectableChannel contiene un blocco su cui è necessario sincronizzare configureBlocking e register. Questo blocco è accessibile anche attraverso il metodo blockingLock(), quindi un altro thread potrebbe bloccare il blocco causando il blocco della chiamata al registro (ma senza traccia dello stack è difficile dirlo).

7

È necessario utilizzare un blocco e sincronizzare manualmente.

nello stesso thread si esegue il ciclo di selezione hanno un ReentrantLock:

final ReentrantLock selectorLock = new ReentrantLock(); 

Poi, quando è necessario registrarsi con il selettore fare qualcosa di simile:

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

Infine, durante il vostro loop che stai chiamando accept(), qualcosa del genere:

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

selector.select(500); 

E t gallina continua con il resto della tua logica.

Questo costrutto garantisce che la chiamata register() non blocca garantendo che non c'è mai un'altra select() tra corrispondenti wakeup() e register() chiamate.

+0

+1 per l'esempio di codice. Io secondo questo, per esperienza. Ben fatto. – casey

+0

Questo aggiunge fino a 500 ms di latenza non necessaria per ogni registrazione. Utilizza invece l'approccio in http://stackoverflow.com/a/2179612/448970. –

+0

@DavidB. Non aggiunge alcuna latenza ad ogni registrazione. È il thread di selezione che blocca fino a 500ms, non il thread di registrazione, e il thread di selezione vorrà sempre bloccare in 'select()' comunque. Le affermazioni nella risposta sono per lo più errate. – EJP

28

Questa è una funzionalità di base della maggior parte delle implementazioni NIO che non è evidente dalla documentazione.

È necessario effettuare tutte le chiamate di registro dallo stesso thread che esegue la selezione o si verificheranno deadlock. Di solito questo viene fatto fornendo una coda di registrazioni/deregistrations/interest-changes su cui è scritto e quindi viene chiamato selector.wakeup(). Quando il thread di selezione si attiva, controlla la coda ed esegue tutte le operazioni richieste.

+1

Questa è una funzionalità di base delle implementazioni * all * NIO completamente specificate in Javadoc. Ci sono tre sincronizzazioni nidificate mentre si è in 'select(),' e 'register()' ne tenta uno. Il risultato non è un deadlock ma un blocco *, che dura solo fino alla chiamata simultanea di 'select()'. La soluzione è chiamare 'wakeup()' prima di 'register(). 'Questo forza il' select() 'per sbloccare e restituire zero, che rilascia i suoi tre lock, il che permette a' register() 'di rivendicare quello di cui ha bisogno e procedere. – EJP

+2

La soluzione fornita nel commento sopra è sbagliata, almeno, data la spiegazione. A seconda della programmazione dei thread, il thread di selezione potrebbe completare un intero ciclo e rientrare in select() prima che il thread di registrazione sia in grado di chiamare register(). – dsd

0

Registrare il canale da qualsiasi thread:

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

il ciclo di selezione dovrebbe essere simile a questo:

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

    .... 
} 
+0

È necessario un solo blocco come mostrato in https://stackoverflow.com/a/1112809/194894 – Flow