Search code examples
javasleep

Why isn't my Thread.sleep() working as intended?


My code currently looks like this, and I want to delay the process AFTER the release code takes place, but before the game starting, but currently it looks as if the release code doesn't occur at all. It looks as if after the delay, the start happens immediately after, which I don't want. If anyone can help, thanks.

This is my code.

startButton.addMouseListener(new MouseListener() {
    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // Press
        startButton.setPreferredSize(new Dimension(240, 240));
        startButton.setIcon(startButtonHover);
        FRAME.pack();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // Release
        // NOT TRIGGERING AT ALL
        startButton.setPreferredSize(new Dimension(256, 256));
        startButton.setIcon(startButtonImage);
        FRAME.pack();

        try {
            Thread.sleep(400);
        } catch (InterruptedException e1) {
            Thread.currentThread().interrupt();
        }

        // Game Start
        FRAME.remove(menuPanel);
        FRAME.revalidate();
        FRAME.repaint();

    }

});

Solution

  • Almost all UI systems, and swing (which is what you are using here) is one of them, has this concept called the Event Dispatcher Thread, or EDT.

    The EDT has a bunch of properties:

    • There is only one such thread.
    • Any interaction with the UI (as in, jLabel.setText("Hello"), removing or adding widgets, changing the colour of something, and so on) must be done from the EDT. If you do it from any other thread, weird things will happen.
    • All event handlers (so, your on mouse click code and such) runs inside the EDT.
    • You must never block the EDT. The EDT is the thread that is taking caring of ensuring your program repaints itself if the user drags another window across your app's window (the OS doesn't remember those pixels! The OS just asks the app to redraw itself; sometimes the mouse pointer isn't a hardware sprite and leaves a 'trail' in its wake). The EDT is also the thread that is taking care of e.g. if the user presses a button but doesn't let go, that the button is re-painted in a 'it is now pressed down' look.

    Ever seen a napp crash such that even pressing buttons doesn't do anything, or the thing just becomes a dull grey colour once you hide it and re-show it and even drag any window across it?

    That'd be an app that violated that rule.

    Here you are sleeping in the EDT. This is forbidden.

    Blocking acts:

    • Talking to a DB
    • Opening a file
    • Interacting with anything on the network
    • Waiting for a thread.
    • A synchronized anything.
    • Thread.sleep
    • and much, much more. Anything that would cause the thread to sleep as it cannot continue until some event occurs (disk ready with bytes, some time passes, a lock becomes free, and so on).

    The solution is not to sleep 400 millisec and then act. The solution is instead to create a new thread which waits 400 milliseconds, and which then injects the code to remove, revalidate, and repaint back into the EDT (after all, that thread is not the EDT, so you can wait 400 millis in it, but you cannot act on the UI, because you must only do that from the EDT).

    This is annoying, but somewhat common, so swing has a thing for it: SwingTimer.