Stop and resume an endless loop while entering a key

Let's say we have an infinite loop and we want it to sleep for a while and then resume it. Instead of having a set time, we resume it when we press a key.

For example, in Java, we could have:

while(true){
    doSomething();
}

      

We can now make it intermittent in many ways. We want to interrupt it with a key press, so we will have:

boolean running = true;
...
this.addKeyListener(this);
...
@override
public void keyPressed(KeyEvent e){
    running = false;
}

      

And then run a method (let's say run

) that contains:

while(running){
    doSomething();
}

      

But then we will face one problem: we will resume.

So we take a method keyPressed

and change its body to:

running = !running;
if(running){
    run();
}

      

There is only one problem here: the KeyListener will not do anything until the method is finished run

. I've tried using streams where we would have:

private class Runner implements Runnable {
    @Override
    public void run() {
        while (running) {
            doSomething();
        }
    }
}

      

and keyPressed

:

if(running){
    runner.wait();
}else{
    runner.notify();
    runner.run();
}
running = !running;

      

but in my actual code, the method doSomething

is code that cannot be interrupted (because it handles the output to the screen), so thread.wait()

it can never be called (it will throw exceptions all the time, not actually wait).

So to summarize: how to stop and resume looping something using an enter key in Java?

+3


source to share


2 answers


wait

and notify

are meant to be called from different threads. As the name suggests, wait

should be called on a thread that is suspended and awaiting notification that a condition has changed:

private final Object keyPressMonitor = new Object();

private boolean running = true;

private Runnable gameLoop = new Runnable() {
    @Override
    public void run() {
        try {
            synchronized (keyPressMonitor) {
                while (true) {
                    while (!running) {
                        keyPressMonitor.wait();
                    }
                    doSomething();
                }
            }
        } catch (InterruptedException e) {
            logger.log(Level.INFO,
                "Interrupted; cleaning up and exiting.", e);
        }
    }
};

      



On another thread, the AWT Event Dispatch Thread appears to be called, in which your KeyListener (or the Action called by the ActionMap / InputMap binding) is called, notifies the loop thread that the proper key has been pressed or released:

public void keyPressed(KeyEvent event) {
    if (event.getKeyCode() == theKeyICareAbout) {
        synchronized (keyPressMonitor) {
            running = true;
            keyPressMonitor.notifyAll();
        }
    }
}

public void keyReleased(KeyEvent event) {
    if (event.getKeyCode() == theKeyICareAbout) {
        synchronized (keyPressMonitor) {
            running = false;
            keyPressMonitor.notifyAll();
        }
    }
}

      

+1


source


You can use Semaphore for these purposes:



private static class Runner implements Runnable {
    private final AtomicInteger permits = new AtomicInteger(0);
    private final Semaphore semaphore = new Semaphore(1, true);
    private volatile boolean running;

    public void putToSleep() {
        semaphore.acquireUninterruptibly();
    }

    public void resume() {
        semaphore.release(permits.getAndSet(0));
    }

    @Override
    public void run() {
        while (running) {
            semaphore.acquireUninterruptibly(Integer.MAX_VALUE);
            semaphore.release(Integer.MAX_VALUE);
            doSomething();
        }
    }

    private void doSomething() {
        //...
    }
}

      

0


source







All Articles