Java 缓冲区策略导致严重滞后

标签 java java-2d

我有一个小型引擎,在我的 OSX 笔记本电脑上运行良好,但在功能较弱的 Linux PC 上运行时会崩溃或严重滞后。我最小化了代码,使其只是一个小类,但仍然存在完全相同的滞后性。我认为这与缓冲区策略和线程有关。这是类(class):

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

最佳答案

我看到 Canvas 被涂成黑色之前有延迟。在将 JFrame 设置为可见之前,您必须在 Canvas 上绘制一些内容。

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

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

  2. 我通过调用 SwingUtilities invokeLater 方法启动了 Swing 应用程序。这将 Swing 组件的创建和执行放在 Event Dispatch thread 上。 。 Oracle 和我坚持所有 Swing 应用程序都以这种方式启动。稍后在本解释中,您将了解为什么这很重要。

  3. 我每 250 毫秒绘制一个白色图像,然后绘制一个黑色图像。这样,我就可以确定绘画代码正在工作。

  4. 我将线程代码移动到它自己的类中。我遇到了线程和 JPanel 使用 boolean 值的问题。通过将线程放入其自己的类中,我可以为线程和 JPanel 创建单独的 boolean 值。

  5. 在 JPanel PaintComponent 方法中,我在将 JFrame 设置为可见之前绘制了一些内容。

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

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

这是代码。

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

    }
}

关于Java 缓冲区策略导致严重滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40025009/

相关文章:

java - 寻找并证明最佳的端口/ socket /线程比

java - BufferStrategy 和 Swing 结合起来?

java - 旋转图像滋扰

java - 将组件绘制到 BufferedImage 会导致显示损坏

java - 如何获得高质量的缩略图

java - Apache Ant 中的 java 任务问题

java - 显示从edittext到textview的文本

java - 在子数组中划分二维数组

java - 在 ubuntu 中运行 Jar 文件时出现问题

Java2D : if statement doesn`t work with java. awt.颜色