编辑:解决了,看下面我的解决方案。
首先,这是我的第一个问题,所以如果我犯了任何错误,请告诉我。
我正在尝试用Java编写Mandelbrot分形程序,用于培训目的。我希望拥有的所有功能的理想选择是Fractalizer(http://www.fractalizer.de/en/),但是现在,我会对在屏幕上绘制Mandelbrot Set的程序感到满意(而不是,例如,将其写入图像文件)。当然,我希望程序快速,所以我认为我可以将计算分成多个线程来利用我的多核处理器;例如,在四核系统上,图像将被分成2×2 = 4个图像,每个图像由单独的线程计算。所有这些线程都会传递一个Graphics对象,并在计算像素时绘制像素。
我的第一次尝试是让线程在BufferedImage.getGraphics()上绘制并让paint()方法不断调用repaint(),只要图像没有完成:
g.drawImage(tempImg, 0, 0, null);
if (waiterThread.isAlive())
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// do nothing
}
repaint(10);
}
(waiterThread一个接一个地连接所有计算线程,所以只要waiterThread存活,至少有一个计算线程尚未完成。)
这很有效,但由于频繁重新绘制,会在画布上造成难看的闪烁。
然后,通过一个小的测试程序,我发现Graphics.draw *任何*在paint方法返回之前立即在屏幕上绘制,所以我当前的方法如下:
不幸的是,这只会创建一个黑屏。仅在计算完成时,才会显示图像。
将多个线程绘制到一个组件上的正确方法是什么?
编辑:
我不知道的是:只允许事件调度线程在AWT组件上绘制(粗略说出),这意味着上面的最后一种方法可能无法工作 - 显然,它是&#39;应该抛出一个例外,但我没有得到一个。我的解决方案是使用第一种方法 - 将图像绘制到BufferedImage上并将其绘制到Canvas上 - 唯一的修改是我重载update()方法以调用paint()方法而不清除绘制区域< / EM>:
public void update(Graphics g)
{
paint(g);
}
所以我想我对一般问题的回答(&#34;我如何让多个线程绘制到AWT组件上?&#34;)将是:你不能,它不是允许。让线程绘制到BufferedImage.getGraphics()上,并重复绘制该图像。像上面一样重载update()方法以避免闪烁。 (它现在看起来真的很棒。)在我的情况下我不能使用的另一个提示,但仍然很好,是有一个repaint()变体,它接受矩形参数来指定必须重绘的区域和一个采用时间参数(以毫秒为单位)的变体,因此重绘不必立即发生。
EDIT2:此链接提供了非常有用的信息:http://java.sun.com/products/jfc/tsc/articles/painting/index.html
答案 0 :(得分:2)
只有GUI线程可以直接在组件上绘制。
所以你必须调用重绘方法。
当你进行背景计算时,为了强制快速绘图,你应该使用version taking a time as parameter。
here的一些细节:
注意:如果在组件之前发生多次调用repaint() 处理初始重绘请求,可以是多个请求 折叠成一次调用update()。算法 确定何时应该折叠多个请求是 实现有关。如果多个请求被折叠,则 结果更新矩形将等于的联合 折叠请求中包含的矩形。
答案 1 :(得分:0)
您必须向EDT发送请求。
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
Rectangle r = myCurrentWorkingThread.getFinishedRectangle();
myPainter.repaint(r);
}
});
这个想法是你不会逐个像素地重新绘制,而是给工作线程提供更大的块。一旦他们完成了一个工作单元,他们就会通知主要对象(myPainter)进行实际工作。这个构造(EventQueue.invokeLater)将保证它将在Event Dispatcher Thread上。