Java thread.sleep (1) sleeping for more than 1ms

I am trying to develop a 2D game using Java. So far, I've been able to set up the game to use fullscreen exclusive mode and to actively render on a user thread. The game loop I chose to use uses a fixed time step variable render type. This type of game loop is supposed to render as fast as the device can handle, which I'm not entirely happy with. So I am trying to limit the frame rate with Thread.sleep()

.

If I turn off all rendering and just refresh the game in a game loop, it Thread.sleep(1)

succeeds about 1 ms

. However, if I turn on rendering, it sometimes Thread.sleep(1)

sleeps longer than 1 ms

eg 15 ms

. I turn rendering on / off by adding / removing lines:

BufferedImage drawImage = render(Math.min(1d, lag / TIME_PER_UPDATE));
drawToScreen(drawImage);

      

What makes the thread sleep too long?

This is my first post on these forums, so please tell me if I did something wrong in my post, or if it is a duplicate (I could not find a similar post).

import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

public class Main implements KeyListener
{

    private static final long serialVersionUID = 1L;
    private boolean gameRunning = false;
    private final double UPDATE_RATE = 60;
    private final double TIME_PER_UPDATE = 1000000000 / UPDATE_RATE;
    private final int MAX_UPDATES_BEFORE_RENDERING = 5;
    private final int TARGET_FPS = 60;
    private int windowWidth;
    private int windowHeight;
    private GraphicsDevice graphicsDevice;
    private DisplayMode defaultDisplayMode;
    private Frame frame;
    private BufferStrategy bufferStrategy;
    private Player player;

    public Main()
    {
        GraphicsDevice[] screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
        this.graphicsDevice = screenDevices[0];

        // This is later used to restore the original display mode when closing
        // the game
        defaultDisplayMode = this.graphicsDevice.getDisplayMode();

        frame = new Frame("GameTest");

        frame.setIgnoreRepaint(true);
        frame.setResizable(false);
        frame.setUndecorated(true);

        // Ensure that the user device supports full screen exclusive mode
        if (this.graphicsDevice.isFullScreenSupported())
        {
            graphicsDevice.setFullScreenWindow(frame);
        }

        windowWidth = frame.getWidth();
        windowHeight = frame.getHeight();

        frame.createBufferStrategy(2);

        bufferStrategy = frame.getBufferStrategy();

        // The frame receives keyboard event dispatched on the EDT-thread.
        frame.addKeyListener(this);

        initGame();

        // Starts the gameThread. The updating of the game state and rendering
        GameThread gameThread = new GameThread();
        gameThread.start();

    }

    private void initGame()
    {
        player = new Player(300, 300);
    }

    private class GameThread extends Thread
    {

        @Override
        public void run()
        {
            gameLoop();
        }
    }

    public static void main(String[] Args)
    {
        new Main();
    }

    private void gameLoop()
    {
        gameRunning = true;

        double lastStartTime = System.nanoTime();
        double startTime;
        double elapsedTime = 0;
        double lag = 0;
        double lastRenderTime;
        int updateCount = 0;

        while (gameRunning)
        {
            System.out.println("");
            System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            System.out.println("New Gameloop");
            System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

            startTime = System.nanoTime();
            elapsedTime = startTime - lastStartTime;
            lag += elapsedTime;

            updateCount = 0;

            while (lag >= TIME_PER_UPDATE && updateCount < MAX_UPDATES_BEFORE_RENDERING)
            {
                updateGameState();
                lag -= TIME_PER_UPDATE;
                updateCount++;
            }

            if (startTime - lastStartTime > TIME_PER_UPDATE)
            {
                lastStartTime = startTime - TIME_PER_UPDATE;
            }

            BufferedImage drawImage = render(Math.min(1d, lag / TIME_PER_UPDATE));
            drawToScreen(drawImage);
            lastRenderTime = System.nanoTime();

            double currentFPS = 1000000000d / (lastRenderTime - startTime);

            //Sleeps until target FPS is reached
            System.out.println("");
            System.out.println("Before sleeping");
            System.out.println("");
            System.out.println("Current FPS:");
            System.out.println(currentFPS);

            while (currentFPS > TARGET_FPS && (lastRenderTime - startTime) < TIME_PER_UPDATE)
            {

                //Lets the CPU rest
                Thread.yield();

                double beginSleepTime = System.nanoTime();

                try
                {

                    Thread.sleep(1);        

                } catch (Exception e)
                {
                    e.printStackTrace();
                }

                double endSleepTime = System.nanoTime();

                lastRenderTime = System.nanoTime();
                currentFPS = 1000000000d / (lastRenderTime - startTime);

                System.out.println("");
                System.out.println("--------------------------------");
                System.out.println("Sleeping");
                System.out.println("");
                System.out.println("Time slept in ms:");
                System.out.println("");
                System.out.println((endSleepTime - beginSleepTime) / 1000000d);
                System.out.println("");
                System.out.println("current FPS");
                System.out.println("");
                System.out.println(currentFPS);     
            }

            lastStartTime = startTime;
        }   
    }

    private void updateGameState()
    {
        player.update();
    }

    private void drawToScreen(BufferedImage drawImage)
    {
        try
        {
            Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();

            g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

            g2d.clearRect(0, 0, windowWidth, windowHeight);
            g2d.setBackground(Color.BLACK);

            g2d.drawImage(drawImage, 0, 0, windowWidth, windowHeight, null);

            g2d.dispose();

            if (!bufferStrategy.contentsLost())
            {
                bufferStrategy.show();
            }

        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private BufferedImage render(double delta)
    {
        BufferedImage drawImage = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_ARGB);
        drawImage.createGraphics();
        Graphics2D g = (Graphics2D) drawImage.getGraphics();

        g.setBackground(Color.WHITE);
        g.clearRect(0, 0, windowWidth, windowHeight);

        //Render player
        g.setColor(Color.BLUE);
        g.fillRect((int) Math.round(player.getLocX() + delta * player.getSpeedX()), (int) Math.round(player.getLocY() + delta * player.getSpeedY()), 64, 64);

        g.dispose();

        return drawImage;
    }

    @Override
    public void keyPressed(KeyEvent keyEvent)
    {
        switch (keyEvent.getKeyCode())
        {

        case KeyEvent.VK_ESCAPE:
            graphicsDevice.setDisplayMode(defaultDisplayMode);
            System.exit(0);
            break;
        case KeyEvent.VK_A:
            player.setSpeedX(-player.getMoveSpeed());
            break;
        case KeyEvent.VK_D:
            player.setSpeedX(player.getMoveSpeed());
            break;
        case KeyEvent.VK_W:
            player.setSpeedY(-player.getMoveSpeed());
            break;
        case KeyEvent.VK_S:
            player.setSpeedY(player.getMoveSpeed());
            break;
        case KeyEvent.VK_SPACE:

            break;
        case KeyEvent.VK_LESS:

            break;
        case KeyEvent.VK_I:

            break;

        }
    }

    @Override
    public void keyReleased(KeyEvent keyEvent)
    {
        switch (keyEvent.getKeyCode())
        {
        case KeyEvent.VK_A:
            player.setSpeedX(0);
            break;
        case KeyEvent.VK_D:
            player.setSpeedX(0);
            break;
        case KeyEvent.VK_W:
            player.setSpeedY(0);
            break;
        case KeyEvent.VK_S:
            player.setSpeedY(0);
            break;
        case KeyEvent.VK_SPACE:

            break;
        case KeyEvent.VK_LESS:

            break;
        case KeyEvent.VK_I:

            break;
        }
    }

    @Override
    public void keyTyped(KeyEvent keyEvent)
    {

    }

    private class Player
    {
        protected double speedX;
        protected double speedY;
        protected double locX;
        protected double locY;
        protected double moveSpeed;

        public Player(int locX, int locY)
        {
            speedX = 0;
            speedY = 0;
            this.locX = locX;
            this.locY = locY;

            moveSpeed = 3d;
        }

        public void update()
        {
            locY +=  speedY;

            locX += speedX;
        }

        public void setSpeedX(double speedX)
        {
            this.speedX = speedX;
        }

        public void setSpeedY(double speedY)
        {
            this.speedY = speedY;
        }

        public double getSpeedX()
        {
            return speedX;
        }


        public double getSpeedY()
        {
            return speedY;
        }

        public double getLocX()
        {
            return locX;
        }

        public double getLocY()
        {
            return locY;
        }

        public double getMoveSpeed()
        {
            return moveSpeed;
        }   
    }
}

      

+3


source to share


3 answers


A method sleep()

in java puts the currently executing thread (running state) to sleep for 1ms.

After 1 ms, the thread comes to the runnable state (it can work ), now it depends on the scheduler when it is necessary to take the thread from the runnable state and execute it (that is, the running state).



For this reason, you can assume that the thread sleeps for at least 1ms before restarting.

Below is a description of the different states of a thread : enter image description here

+6


source


The java docs clearly state (for thread.sleep ())

Causes the currently executing thread to sleep (temporarily stop execution) in the specified number of milliseconds plus the specified number of nanoseconds, taking into account the precision and accuracy of system timers and schedulers.



You are at the mercy of scheduling and system time in the system. The only way to guarantee the time is to make sure that the thread is blocking its execution and cannot be started, but this will create problems elsewhere.

For which I believe it is generally bad to sleep in threads for a fixed amount of time, unless there is an absolute reason to do so things should be triggered by other events not at fixed intervals.

0


source


According to the javadoc:

Thread.sleep()

Causes the currently executing thread to sleep (temporarily stop execution) in the specified number of milliseconds, taking into account the accuracy and precision of the system timers and schedulers.

Thus, it Thread.sleep(ms)

has low accuracy.

Second, note that this method throws a checked exception ThreadInterruptedException

. Which can be caused by false awakening . Thus, it Thread.sleep(1000)

may even terminate immediately after ms.

An alternative solution with better accuracy is LockSupport.parkNanos()

. But also with this method you have to watch for interrupts of other threads. PS: There is also Thread.sleep(ms,nanos)

one that has the same low precision as Thread.sleep(ms)

(nano seconds are rounded to ms).

0


source







All Articles