Java thread.sleep(1) sleep 时间超过 1 毫秒

标签 java multithreading

我正在尝试使用 Java 开发 2D 游戏。到目前为止,我已经设法将游戏设置为使用全屏独占模式并在自定义线程中进行主动渲染。我决定使用的游戏循环属于固定时间步变量渲染类型。这种类型的游戏循环应该在设备可以处理的情况下尽可能快地呈现,我对此并不完全满意。所以我尝试使用 Thread.sleep() 来限制帧速率。

如果我关闭所有渲染,并简单地在游戏循环中更新游戏,Thread.sleep(1) 会成功 hibernate 大约 1 毫秒。但是,如果我打开渲染,有时 Thread.sleep(1) hibernate 的时间会超过 1 毫秒,比如 15 毫秒。 我通过添加/删除线条来打开/关闭渲染:

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

是什么导致线程 hibernate 时间过长?

这是我第一次在这些论坛上发帖,所以请告诉我我的帖子是否做错了什么,或者这是重复的(我没有找到类似的帖子)。

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;
        }   
    }
}

最佳答案

java中的sleep()方法让当前正在执行的线程(处于运行状态) hibernate 1ms。

1 毫秒后,线程进入可运行状态(能够运行),现在取决于调度程序何时从可运行状态获取线程并执行它(即运行状态)。

因此,您可以假设线程在再次运行之前至少 hibernate 1 毫秒。

下图描述了不同的线程状态: enter image description here

关于Java thread.sleep(1) sleep 时间超过 1 毫秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30034457/

相关文章:

java - 尝试生成字符串作为 Word Solve 解决方案的提示

c# - 查看我的应用程序正在运行多少个线程?

c# - 从一个额外的线程返回结果

java - ThreadLocal 变量的性能

Java,将值传递给实现 Callable 的类的构造函数

java/lang/NoClassDefFoundError : java/lang/invoke/MethodHandle eclipse juno 错误

java - 实现方法抛出异常,但接口(interface)方法未定义,因为它抛出异常

Java强制使用枚举开关从方法返回默认语句,并覆盖所有枚举值

java - 如何使用 JSSE 实现在 tomcat 8.5.5 中启用 TLSv1.3

ios - 在单独的线程中循环位置数组