我正在用 Java 制作一个概念验证游戏,并决定(为了练习,但主要是为了好玩)制作我自己的具有线程安全双缓冲的简单组件。但是,我在并发方面(特别是 Swing)经验不是很丰富,我想知道我的实现是否存在任何我遗漏的问题。
实现如下:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class GamePanel extends JPanel
{
private static final long serialVersionUID = 1L;
private BufferedImage mBackImage = null;
private BufferedImage mFrontImage = null;
public BufferedImage getBackImage ()
{
return mBackImage;
}
public void swap ()
{
BufferedImage new_back;
//
synchronized (this)
{
new_back = mFrontImage;
mFrontImage = mBackImage;
}
//
int width = getWidth (), height = getHeight ();
if (width > 0 && height > 0)
{
if (new_back == null || new_back.getWidth () != width
|| new_back.getHeight () != height)
new_back = new BufferedImage (width, height,
BufferedImage.TYPE_INT_ARGB);
//
mBackImage = new_back;
}
else
mBackImage = null;
}
@Override
public void paintComponent (Graphics g)
{
synchronized (this)
{
if (mFrontImage == null)
super.paintComponent (g);
else
g.drawImage (mFrontImage, 0, 0, null);
}
}
}
我假设 getBackImage()
和 swap()
只会由单个线程(游戏循环线程)调用。油漆是通过 Swing 计时器触发的,因此在 EDT 中也是如此。我相信使用 mFrontImage 的简单同步块(synchronized block)应该足以防止不必要的行为,允许游戏循环线程渲染到背面图像并调用交换,而不必担心 Swing 重绘。我错过了什么吗?
最佳答案
在游戏线程中:
BufferedImage image = gamePanel.getBackPanel();
gamePanel.swap();
您的程序现在将处于事件调度队列和游戏线程可以访问同一图像的状态。这意味着如果您在 EDT 将图像绘制到屏幕上的同时绘制图像,则有趣的东西可能会绘制到面板上。
为了单独读取和写入 frontImage 字段,那么您是线程安全的,因为所有访问都是在同步块(synchronized block)内完成的。但是,可以更好地编写 PaintComponent 方法,以减少在同步块(synchronized block)中花费的时间。即:
@Override
public void paintComponent (Graphics g)
{
BufferedImage localFrontImage;
synchronized (this)
{
localFrontImage = mFrontImage;
}
if (localFrontImage == null)
super.paintComponent (g);
else
g.drawImage (localFrontImage, 0, 0, null);
}
关于java - 这个简单的基于 Swing 的类是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9385697/