9
votes

Comment interrompre Java.Util.Scanner Appel NextLine

J'utilise un environnement multi-threadé, un thread est en constante écoute pour l'entrée de l'utilisateur en appelant à plusieurs reprises scanner.nextline () . Pour mettre fin à l'application, cette analyse est arrêtée par un autre thread, mais le thread d'écoute ne s'arrête pas jusqu'à ce qu'une dernière entrée de l'utilisateur ait été faite (en raison de la nature bloquante de nextline () ).

La fermeture du flux semble ne pas être une option depuis que je lise de System.in , qui renvoie un introuvable qui n'est pas resserrable.

Y a-t-il un moyen d'interrompre le blocage du scanner, de sorte qu'il reviendra?

merci


4 commentaires

Vous pouvez appeler scanner.hasnext () au lieu de scanner.nextline () cette méthode mai bloc selon la Javadoc, vous pourriez donc avoir besoin de gérer cela. L'idée est que contrairement à scanner.nextline () , scanner.hasnext () Ne pas avancer l'entrée, vous pouvez donc vérifier un indicateur si le fil de lecture a été arrêté par Un autre fil avant d'appeler scanner.nextline ()


Oui, mais cela impliquerait un sondage constant.


Vous devriez pouvoir appeler le fil.Interrompt sur le thread d'écoute, cela entraînerait une introduction d'une interrupteIoException que vous pouvez obtenir de la méthode IoException (). Je ne sais pas comment il interagit avec NextLine () ou s'il fonctionne avec votre intrigue sous-jacente, mais il devrait terminer dans la plupart des cas.


@Josefx Selon mes tests, cela ne termine pas Nextline - toujours. Au moins pas pour moi.


3 Réponses :


10
votes

Ce Article Décrit une approche pour éviter le blocage lors de la lecture. Il donne à l'extrait de code, que vous pourriez modifier comme je l'indique dans un commentaire. XXX

Vous pouvez utiliser ce code directement ou écrire une nouvelle classe d'introuvream fermée, en appuyant sur la logique décrite dans Cet article.


1 commentaires

Hé merci, j'espérais une solution qui n'implique pas d'attente active.



-3
votes

Bien sûr. Utiliser un nuke. Appelez system.exit (0) à la fin de votre fil principal. Cela meurrera tout. Même le fil actif en attente dans le système.in.

Le problème est que System.in est un flux d'entrée traditionnel avec blocage, et lorsqu'il bloque le thread est marqué comme en cours d'exécution. Vous ne pouvez pas l'interrompre. Donc, quel que soit le fil que vous utilisez pour lire le système.in appelle la lecture et la lecture bloquera le fil. Vous pouvez coaxer une partie de ces choses avec un tas d'astuces évitez de faire appel à la lecture sauf dans les cas où nous pouvons être sûrs qu'il n'y aura pas de bloc, puis constamment sonder. Mais, il n'y a pas de manière réelle autour du problème que toute tentative de lecture qui verrouille votre fil et aucune quantité de fermeture de flux sous-jacents ou n'interrompre ni l'arrêt du thread vous sauvera. Mais si vous meurez tout le VM ... le fil mourra.

Évidemment, vous devez vous assurer que le reste des threads est correctement sorti et que c'est juste qu'un stupide, je souhaite pouvoir répondre au fil d'entrée tapé qui est le dernier cintre. Mais, si c'est totalement le cas, la réponse correcte est de sortir, ou du moins au moins, la seule réponse qui fonctionnera sans brûler les cycles d'horlogerie sans raison et que le programme se termine.


2 commentaires

J'ai effectivement essayé cela, et system.exit (0) n'a pas eu l'empêchait d'étonnamment. J'ai dû tuer -9 le processus. Même killall Java n'a pas fonctionné.


Cela a travaillé pour moi. Mais, malheureusement avec ce bug / échec, j'irais que cela pourrait bien être immunitaire Nuke.



2
votes

Pour commencer avec: Cela ne résoudra pas le problème qui, pour fermer l'ensemble du programme, nécessite un appel système.exit () s'il y a eu une demande d'entrée non remplie (même si annulée). Vous pouvez potentiellement em> le contourner en surfant une frappe à une frappe dans la console, mais c'est un tout autre parc à balles.

Si vous voulez le faire dans la console, est impossible à faire sans interrogation, car il est impossible de faire En réalité, non bloquer un fil en attente d'une entrée de System.in, comme système. À cause de cela, sans utiliser l'interrogation pour seulement demander une entrée si vous savez qu'il ne bloquera pas. P>

Si vous voulez vraiment quelque chose qui agira comme une console interruptible () pour une console, vous devriez probablement examiner Faire une fenêtre de swing, ou similaire, et faire une simple interface d'entrée pour elle. Ce n'est pas vraiment difficile et aurait toutes les fonctionnalités que vous demandez, en dehors de certains cas de bord. P>

Cependant, je travaillais moi-même, car je voulais un moyen pour un fil de fil Arrêtez d'attendre l'attente de l'entrée de System.in, sans fermer le programme (et tout en évitant le sondage), et c'est ce que j'ai proposé, avant de passer à ma propre fenêtre. P>

Je ne peux pas dire avec aucun La confiance, c'est la meilleure pratique, mais il devrait être le fil-faire, semble aller bien fonctionner et je ne peux penser à aucun problème immédiat. Je voudrais basculer des échecs à partir de sorties alternatives (bien que, sinones non obsolètes), à des erreurs réelles. Vous pouvez annuler des demandes actives d'entrée soit en interrompant le thread, soit en appelant Annuler (), qui annule la demande d'attente actuellement. P>

Il utilise des sémaphores et des threads pour créer une méthode NextLlline () qui peut être interrompu / annulé ailleurs. L'annulation n'est pas parfaite - vous ne pouvez annuler que la demande du fil d'attente, par exemple, mais l'interruption de threads doit fonctionner correctement. P>

package testapp;

/**
 *
 * @author Devlin Grasley
 */
import java.util.concurrent.Semaphore;
import java.util.Scanner;

public class InterruptableSysIn {
    protected static Scanner input = new Scanner (System.in);
    protected static final Semaphore waitingForInput = new Semaphore(0,true); //If InterruptableSysIn is waiting on input.nextLine(); Can also be cleared by cancel();
    protected static String currentLine = ""; //What the last scanned-in line is
    private static final Input inputObject = new Input();
    private static final Semaphore waitingOnOutput = new Semaphore (1); // If there's someone waiting for output. Used for thread safety
    private static boolean canceled = false; //If the last input request was cancled.
    private static boolean ignoreNextLine = false; //If the last cancel() call indicated input should skip the next line.
    private static final String INTERRUPTED_ERROR = "\nInterrupted";
    private static final String INUSE_ERROR = "\nInUse";
    private static boolean lasLineInterrupted = false;

    /**
     * This method will block if someone else is already waiting on a next line.
     * Gaurentees on fifo order - threads are paused, and enter a queue if the
     * input is in use at the time of request, and will return in the order the
     * requests were made
     * @return The next line from System.in, or "\nInterrupted" if it's interrupted for any reason
     */
    public static String nextLineBlocking(){
        //Blocking portion
        try{
            waitingOnOutput.acquire(1);
        }catch(InterruptedException iE){
            return INTERRUPTED_ERROR;
        }
        String toReturn = getNextLine();
        waitingOnOutput.release(1);
        return toReturn;
    }

    /**
     * This method will immediately return if someone else is already waiting on a next line.
     * @return The next line from System.in, or 
     * "\nInterrupted" if it's interrupted for any reason
     * "\nInUse" if the scanner is already in use
     */
    public static String nextLineNonBlocking(){
        //Failing-out portion
        if(!waitingOnOutput.tryAcquire(1)){
            return INUSE_ERROR;
        }
        String toReturn = getNextLine();
        waitingOnOutput.release(1);
        return toReturn;
    }

    /**
     * This method will block if someone else is already waiting on a next line.
     * Gaurentees on fifo order - threads are paused, and enter a queue if the
     * input is in use at the time of request, and will return in the order the
     * requests were made
     * @param ignoreLastLineIfUnused If the last line was canceled or Interrupted, throw out that line, and wait for a new one.
     * @return The next line from System.in, or "\nInterrupted" if it's interrupted for any reason
     */
    public static String nextLineBlocking(boolean ignoreLastLineIfUnused){
        ignoreNextLine = ignoreLastLineIfUnused;
        return nextLineBlocking();
    }

    /**
     * This method will fail if someone else is already waiting on a next line.
     * @param ignoreLastLineIfUnused If the last line was canceled or Interrupted, throw out that line, and wait for a new one.
     * @return The next line from System.in, or 
     * "\nInterrupted" if it's interrupted for any reason
     * "\nInUse" if the scanner is already in use
     */
    public static String nextLineNonBlocking(boolean ignoreLastLineIfUnused){
        ignoreNextLine = ignoreLastLineIfUnused;
        return nextLineNonBlocking();
    }

    private static String getNextLine(){
        String toReturn = currentLine; //Cache the current line on the very off chance that some other code will run etween the next few lines

        if(canceled){//If the last one was cancled
            canceled = false;

            //If there has not been a new line since the cancelation
            if (toReturn.equalsIgnoreCase(INTERRUPTED_ERROR)){
                //If the last request was cancled, and has not yet recieved an input

                //wait for that input to finish
                toReturn = waitForLineToFinish();
                //If the request to finish the last line was interrupted
                if(toReturn.equalsIgnoreCase(INTERRUPTED_ERROR)){
                    return INTERRUPTED_ERROR;
                }

                if(ignoreNextLine){
                    //If the last line is supposed to be thrown out, get a new one
                    ignoreNextLine = false;
                    //Request an input
                    toReturn = getLine();
                }else{
                    return toReturn;
                }

            //If there has been a new line since cancelation
            }else{
                //If the last request was cancled, and has since recieved an input
                try{
                    waitingForInput.acquire(1); //Remove the spare semaphore generated by having both cancel() and having input
                }catch(InterruptedException iE){
                    return INTERRUPTED_ERROR;
                }

                if(ignoreNextLine){
                    ignoreNextLine = false;
                    //Request an input
                    toReturn = getLine();
                }
                //return the last input
                return toReturn;
            }
        }else{
            if(lasLineInterrupted){

                //wait for that input to finish
                toReturn = waitForLineToFinish();
                //If the request to finish the last line was interrupted
                if(toReturn.equalsIgnoreCase(INTERRUPTED_ERROR)){
                    return INTERRUPTED_ERROR;
                }

                //Should the read be thrown out?
                if(ignoreNextLine){
                    //Request an input
                    toReturn = getLine();
                }

            }else{
                ignoreNextLine = false; //If it's been set to true, but there's been no cancaleation, reset it.

                //If the last request was not cancled, and has not yet recieved an input
                //Request an input
                toReturn = getLine();
            }
        }
        return toReturn;
    }

    private static String getLine (){
        Thread ct = new Thread(inputObject);
        ct.start();
        //Makes this cancelable
        try{
            waitingForInput.acquire(1); //Wait for the input
        }catch(InterruptedException iE){
            lasLineInterrupted = true;
            return INTERRUPTED_ERROR;
        }
        if(canceled){
            return INTERRUPTED_ERROR;
        }
        return currentLine;
    }

    public static String waitForLineToFinish(){
        //If the last request was interrupted
        //wait for the input to finish
        try{
            waitingForInput.acquire(1);
            lasLineInterrupted = false;
            canceled = false;
            return currentLine;
        }catch(InterruptedException iE){
            lasLineInterrupted = true;
            return INTERRUPTED_ERROR;
        }
    }

    /**
     * Cancels the currently waiting input request
     */
    public static void cancel(){
        if(!waitingOnOutput.tryAcquire(1)){ //If there is someone waiting on user input
            canceled = true;
            currentLine = INTERRUPTED_ERROR;
            waitingForInput.release(1); //Let the blocked scanning threads continue, or restore the lock from tryAquire()    
        }else{
            waitingOnOutput.release(1); //release the lock from tryAquire()    
        }
    }

    public static void cancel(boolean throwOutNextLine){
        if(!waitingOnOutput.tryAcquire(1)){ //If there is someone waiting on user input
            canceled = true;
            currentLine = INTERRUPTED_ERROR;
            ignoreNextLine = throwOutNextLine;
            waitingForInput.release(1); //Let the blocked scanning threads continue
        }else{
            waitingOnOutput.release(1); //release the lock from tryAquire()    
        }
    }

}

class Input implements Runnable{
    @Override
    public void run (){
        InterruptableSysIn.currentLine = InterruptableSysIn.input.nextLine();
        InterruptableSysIn.waitingForInput.release(1); //Let the main thread know input's been read
    }

}


0 commentaires