Java缓冲策略导致严重滞后

时间:2016-10-13 15:21:22

标签: java java-2d

我有一个在我的OSX笔记本电脑上运行良好的小型引擎,但在功能较弱的Linux PC上运行时会严重崩溃或严重滞后。我把代码最小化了,它只是一个小类,但完全相同的lagginess就在那里。我认为这与缓冲策略和线程有关。这是班级:

public class Test extends Canvas implements Runnable {

    private Thread thread;
    private boolean running = false;

    public Test()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(1000, 1000));
        frame.add(this);
        frame.setVisible(true);
        start();
    }

    public static void main(String[] args)
    {
        new Test();
    }

    public synchronized void start()
    {
        this.thread = new Thread(this);
        this.thread.start();
        this.running = true;
    }

    public synchronized void stop()
    {
        try
        {
           this.thread.join();
           this.running = false;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public void run()
    {
        while(running)
        {
            render();
        }
    }

    private void render()
    {
        BufferStrategy bs = this.getBufferStrategy();
        if(bs == null)
        {
            this.createBufferStrategy(2);
            return;
        }

        Graphics g = bs.getDrawGraphics();
        g.setColor(Color.black);
        g.fillRect(0, 0, 1000, 1000);
        g.dispose();
        bs.show();
    }
}

1 个答案:

答案 0 :(得分:2)

我看到Canvas被漆成黑色之前的延迟。在将JFrame设置为可见之前,您必须在Canvas上绘制一些东西。

以下是我对您的代码所做的更改。

  1. 我使用了JPanel而不是Canvas。我使用JPanel获得自动双缓冲。

  2. 我通过调用SwingUtilities invokeLater方法启动了Swing应用程序。这将Swing组件的创建和执行放在Event Dispatch thread上。 Oracle和我坚持认为所有Swing应用程序都是以这种方式启动的。在这个解释的后面,你会明白为什么这是至关重要的。

  3. 我每250毫秒绘制一张白色图像,然后绘制一张黑色图像。这样,我确信绘画代码正常运作。

  4. 我将线程代码移到了自己的类中。我有一个问题,线程和JPanel使用布尔值通过将线程放在自己的类中,我可以为线程和JPanel创建单独的布尔值。

  5. 在JPanel paintComponent方法中,我在设置JFrame之前绘制了一些东西。

  6. 我设置了JPanel的首选大小,而不是JFrame的大小。无论如何,我想知道绘图板的大小。

  7. 在线程代码中,对绘图的方法调用JPanel放在SwingUtilities invokeLater方法内。这确保了绘制发生在Event Dispatch线程上,而定时循环在不同的线程中执行。这可确保GUI保持响应。

  8. 这是代码。

    package com.ggl.testing;
    
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class DrawingTest extends JPanel {
        private static final long serialVersionUID = 2584117430541789858L;
    
        private DrawingTestRunnable drawingTestRunnable;
    
        private boolean isWhite;
    
        public DrawingTest() {
            this.setPreferredSize(new Dimension(1200, 700));
            this.isWhite = true;
    
            JFrame frame = new JFrame("Drawing Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(this);
            frame.pack();
            frame.setVisible(true);
            start();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new DrawingTest();
                }
            });
        }
    
        public synchronized void start() {
            drawingTestRunnable = new DrawingTestRunnable(this);
            new Thread(drawingTestRunnable).start();
        }
    
        public boolean isWhite() {
            return isWhite;
        }
    
        public void setWhite(boolean isWhite) {
            this.isWhite = isWhite;
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
    
            if (isWhite) {
                g.setColor(Color.WHITE);
            } else {
                g.setColor(Color.BLACK);
            }
    
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            g.dispose();
        }
    
        public class DrawingTestRunnable implements Runnable {
            private boolean isWhite;
            private volatile boolean running;
    
            private DrawingTest drawingTest;
    
            public DrawingTestRunnable(DrawingTest drawingTest) {
                this.drawingTest = drawingTest;
                this.running = true;
            }
    
            @Override
            public void run() {
                long duration = 250L;
                long startTime = System.currentTimeMillis();
                while (running) {
                    repaintPanel();
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    long loopDuration = Math.max((duration - elapsedTime), 5L);
                    sleep(loopDuration);
                    startTime = System.currentTimeMillis();
                }
            }
    
            private void repaintPanel() {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        drawingTest.repaint();
                        isWhite = !isWhite;
                        drawingTest.setWhite(isWhite);
                    }
                });
            }
    
            private void sleep(long duration) {
                try {
                    Thread.sleep(duration);
                } catch (InterruptedException e) {
    
                }
            }
    
            public synchronized void setRunning(boolean running) {
                this.running = running;
            }
    
        }
    }