如何让多个Threads绘制到AWT组件上?

编辑:解决了,看下面我的解决方案。

首先,这是我的第一个问题,所以如果我犯了任何错误,请告诉我。

我正在尝试用Java编写Mandelbrot分形程序,用于培训目的。 我想要的所有function的理想选择是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存活,至少有一个计算线程尚未完成。)

这有效,但由于经常重新绘制,会在canvas上造成难看的闪烁。

然后,通过一个小的测试程序,我发现Graphics.draw *任何*在paint方法返回之前立即在屏幕上绘制,所以我当前的方法如下:

  • 一个GridLayout包含2×2的面板(在<4核系统上,1×1)MandelbrotCanvas对象
  • 每个MandelbrotCanvas对象将在第一次paint()调用时初始化一个计算Thread,将其自己的Graphics对象传递给它(实际上,我使用的是一个自定义的GroupGraphics类,它将一个Graphics调用传递给多个图形,以“备份”将图像转换为BufferedImage.getGraphics(),但这并不重要),并启动计算线程。
  • 面板将在其paint()方法中从每个MandelbrotCanvases中获取计算线程并加入()它们。

不幸的是,这只会产生黑屏。 仅在计算完成时,才会显示图像。

将多个线程绘制到一个组件上的正确方法是什么?

编辑:

我不知道的事情:只允许事件调度线程在AWT组件上绘制(粗略说出),这意味着上面的最后一种方法不可行 – 显然,它应该抛出exception,但我没有’得到一个。 我的解决方案是使用第一种方法 – 将图像绘制到BufferedImage上并将其绘制到Canvas上 – 唯一的修改是我重载update()方法以调用paint()方法而不清除绘制区域

 public void update(Graphics g) { paint(g); } 

所以我想我对一般问题的答案(“如何让多个线程绘制到AWT组件上?”)将是:你不能,这是不允许的。 让线程绘制到BufferedImage.getGraphics()上,并重复绘制该图像。 像上面一样重载update()方法以避免闪烁。 (它现在看起来真的很棒。)我不能在我的情况下使用的另一个提示,但仍然很好,是有一个repaint()变体,它接受矩形参数来指定必须重绘的区域,以及一个采用时间参数(以毫秒为单位)的变体,因此重绘不必立即发生。

EDIT2:此链接提供了非常有用的信息: http : //java.sun.com/products/jfc/tsc/articles/painting/index.html

只有GUI线程可以直接在组件上绘制。

所以你必须调用重绘方法。

当你有背景计算时,为了强制快速绘图,你应该使用花时间作为参数的版本 。

这里有一些细节:

注意:如果在处理初始重绘请求之前在组件上发生多次调用repaint(),则多个请求可能会折叠为一次调用update()。 用于确定何时应该折叠多个请求的算法是依赖于实现的。 如果折叠了多个请求,则生成的更新矩形将等于折叠请求中包含的矩形的并集。

您必须向EDT发送请求。

  EventQueue.invokeLater(new Runnable() { @Override public void run() { Rectangle r = myCurrentWorkingThread.getFinishedRectangle(); myPainter.repaint(r); } }); 

这个想法是你不会逐个像素地重新绘制,而是给工作线程提供更大的块。 一旦他们完成了一个工作单元,他们就会通知主要对象(myPainter)进行实际工作。 此构造(EventQueue.invokeLater)将保证它将在Event Dispatcher Thread上。