Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我在BuffereImage中有奇怪的黑色区域,如何修复?_Java_Swing_Concurrency_Awt_Bufferedimage - Fatal编程技术网

Java 我在BuffereImage中有奇怪的黑色区域,如何修复?

Java 我在BuffereImage中有奇怪的黑色区域,如何修复?,java,swing,concurrency,awt,bufferedimage,Java,Swing,Concurrency,Awt,Bufferedimage,我目前正在编写一个分形资源管理器程序,我遇到了一个非常奇怪的问题:我在一个BuffereImage上绘制分形,我在该图像中得到随机的黑色区域。截图: 图像是多线程计算的:大图像被分割成四个子图像(因为我有一个四核处理器),分别计算。黑色区域显示在每个子图像的开头。它们总是矩形的,不一定遵循像素的计算顺序(从左到右,但区域并不总是延伸到子图像的远侧) 我已经验证过,在绘制完像素后(使用Graphics.drawLine),BufferedImage.getRGB立即返回像素的正确颜色,但在计算完成

我目前正在编写一个分形资源管理器程序,我遇到了一个非常奇怪的问题:我在一个BuffereImage上绘制分形,我在该图像中得到随机的黑色区域。截图:

图像是多线程计算的:大图像被分割成四个子图像(因为我有一个四核处理器),分别计算。黑色区域显示在每个子图像的开头。它们总是矩形的,不一定遵循像素的计算顺序(从左到右,但区域并不总是延伸到子图像的远侧)

我已经验证过,在绘制完像素后(使用Graphics.drawLine),BufferedImage.getRGB立即返回像素的正确颜色,但在计算完成后,它可以返回黑色,因为像素是在屏幕上绘制的

如果我禁用多线程计算(通过任务管理器仅将一个内核分配给javaw.exe),问题似乎就会消失,但我真的不想放弃多核计算。还有其他人遇到过这个问题吗(我没有通过谷歌和stackoverflow找到任何东西),你知道如何解决它吗

Graphics.drawLine调用在图形对象上同步;如果我在BuffereImage上另外同步了它,则不会有任何更改


如果你想亲眼看到这个bug,你可以在下载程序。它也可以在GitHub上获得(https://github.com/lucaswerkmeister/JFractalizer),但我最近才转到GitHub,在第一次GitHub提交中,问题已经很明显。

我认为问题在于BuffereImage和Graphics都不是线程安全的,并且在计算后读取BuffereImage的线程中会看到过时的值

像你说的那样在BuffereImage上同步应该会有帮助。但是请注意,您必须同步来自所有线程的所有访问,包括只读访问。因此,我的猜测是,在某个组件(应该是AWT线程)上绘制BuffereImage的线程不进行同步,因此会看到过时的值

但是,我建议不要在多个线程之间共享一个BuffereImage,而是为每个线程提供一个单独的映像,让它可以在上面绘制。然后,在所有线程完成后,将它们的工作合并到AWT线程中的新图像上

此外,如果您还没有这样做,我建议您使用一个。它的优点是,任务返回值的可见性问题(在您的示例中是工作线程的映像部分)由库类处理


如果将这两种方法结合使用,则无需进行任何手动同步,这始终是一件好事(因为很容易出错)。

缓冲图像可能不是线程安全的,因为它们的数据可能存在于图形卡上。但是,这可以被覆盖。通过使用
((DataBufferInt)image.getRaster().getDataBuffer()).getData()
用于高速完整图像绘制的秘密技术(数据缓冲区类型取决于您选择的图像类型),您将获得一个未加速的图像。只要您从未对同一像素写入两次,理论上这应该是完全安全的。但是记住在从图像中读取像素之前以某种方式连接线程。实际上不建议使用来自线程的join()方法,因为这需要线程死亡

相关说明:
您所看到的闪烁可能是awt在屏幕上呈现方式的产物。它在即时模式下运行,这意味着您执行的每个绘图操作都会立即更新屏幕。这会降低多个对象直接渲染到窗口的速度。您可以通过实现双缓冲策略来避免闪烁。我喜欢绘制中间图像,然后仅将该图像绘制到屏幕。

在绘制方法中同步对BuffereImage的访问没有帮助。此外,黑色区域不是临时性的——即使在所有可能并发访问BuffereImage的操作早已停止(例如,当我使用ImageIO保存图像时),它们仍然存在。我想我可以给每个线程自己的图像,但我想看到计算的进度,而它还没有完成。我会在一分钟内检查它是否有效。(继续前面的评论)不,我没有使用ExecutorService,我甚至不知道它。(我怀疑有一些东西可以简化多线程编程,但我从来没有在大型程序中使用过Java——见鬼,我还是个学生——我想只要简单线程有效,我就会使用它。我想我到时候会检查这些东西。)第一个报告:我修改了它,这样每个线程现在都可以直接引用普通图像(还没有自己的图像),并在自己的createGraphics上绘制。速度要快得多,但黑色区域的增加量相同。我知道这会更快,因为现在同步是无用的,但我认为一旦我去掉了黑色区域,丢失的同步就不会成为问题:黑色区域只在开始时出现,而在图像中后期,并发绘制调用似乎根本不会干扰BuffereImage。(我上传了第三个截图。)好的,我改变了它,现在每个线程实际上都在自己的图像上绘制,性能又有了很大的提高——谢谢你!它还消除了黑色区域。图像仍在同时计算和绘制时会出现一些闪烁,但这是暂时的。接下来我将研究这个Executor服务,让我们看看它的作用。无论如何,谢谢你!