Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/369.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 AWT drawImage竞争条件-如何使用synchronized来避免它_Java_Asynchronous_Awt_Synchronized_Doublebuffered - Fatal编程技术网

Java AWT drawImage竞争条件-如何使用synchronized来避免它

Java AWT drawImage竞争条件-如何使用synchronized来避免它,java,asynchronous,awt,synchronized,doublebuffered,Java,Asynchronous,Awt,Synchronized,Doublebuffered,经过数小时的调试和分析,我终于找到了引起竞争状况的原因。解决它是另一回事 为了查看运行中的竞争条件,我在调试过程中录制了一段视频。此后,我进一步了解了情况,因此请原谅糟糕的评论和调试过程中实施的愚蠢机制 因此,情况是:我们有一个曲面(即java.awt.Frame或Window)的双缓冲实现,其中有一个正在进行的线程,该线程基本上连续循环,调用渲染过程(执行UI布局并将其渲染到backbuffer),然后在渲染后将渲染区域从backbuffer渲染到屏幕 以下是双缓冲渲染的伪代码版本(第824

经过数小时的调试和分析,我终于找到了引起竞争状况的原因。解决它是另一回事

为了查看运行中的竞争条件,我在调试过程中录制了一段视频。此后,我进一步了解了情况,因此请原谅糟糕的评论和调试过程中实施的愚蠢机制

因此,情况是:我们有一个曲面(即java.awt.Frame或Window)的双缓冲实现,其中有一个正在进行的线程,该线程基本上连续循环,调用渲染过程(执行UI布局并将其渲染到backbuffer),然后在渲染后将渲染区域从backbuffer渲染到屏幕

以下是双缓冲渲染的伪代码版本(第824行的完整版本):

public RenderedRegions render() {
    // pseudo code
    RenderedRegions r = super.render();
    if (r==null) // nothing rendered
        return
    for (region in r)
        establish max bounds
    blit(max bounds)
    return r;
}
与任何AWT曲面实现一样,它还实现(AWT.java中的第507行-link limit:(-use surface.java link,将core/surface.java替换为plat/AWT.java)绘制/更新覆盖,该覆盖也会从backbuffer到屏幕:

        public void paint(Graphics gr) {
            Rectangle r = gr.getClipBounds();
            refreshFromBackbuffer(r.x - leftInset, r.y - topInset, r.width, r.height);
        }
使用drawImage()函数实现Blitting(AWT.java中的第371行):

    /** synchronized as otherwise it is possible to blit before images have been rendered to the backbuffer */
    public synchronized void blit(PixelBuffer s, int sx, int sy, int dx, int dy, int dx2, int dy2) {
        discoverInsets();
        try {
            window.getGraphics().drawImage(((AWTPixelBuffer)s).i,
                              dx + leftInset, dy + topInset,     // destination topleft corner
                              dx2 + leftInset, dy2 + topInset,   // destination bottomright corner
                              sx, sy,                            // source topleft corner
                              sx + (dx2 - dx), sy + (dy2 - dy),  // source bottomright corner
                              null);
        } catch (NullPointerException npe) { /* FIXME: handle this gracefully */ }
    }
(警告:这就是我开始做出假设的地方!)

这里的问题似乎是drawImage是异步的,通过paint/update从refreshBackBuffer()发出的blit首先被调用,但随后发生

所以…blit已经同步了。防止竞争状况的明显方法不起作用:(

到目前为止,我提出了两种解决方案,但都不理想:

  • 在下一个渲染过程中重新渲染
    缺点:性能受到影响,在遇到竞争条件时仍然会有一点闪烁(有效屏幕->无效屏幕->有效屏幕)

  • 不要在绘制/更新时闪烁,而是设置刷新边界并在下一个渲染过程中使用这些边界
    缺点:当屏幕无效且主应用程序线程正在追赶时,会出现黑色闪烁

  • 这里(1)似乎是两个弊病中较小的一个。编辑:和(2)不起作用,得到空白屏幕…(1)效果很好,但只是掩盖了潜在的问题

    由于我对synchronized以及如何使用synchronized的理解不足,我希望并似乎无法想象的是一种锁定机制,它以某种方式解释了drawImage()的异步特性

    或者使用ImageObserver


    请注意,由于应用程序的性质(Vexi,对于那些感兴趣的人,网站已经过时,我只能使用2个超链接),渲染线程必须在绘制/更新之外-它有一个单线程脚本模型和布局过程(渲染的子过程)调用脚本。

    不确定,但如果在AWT绘制线程中执行Blit,会发生什么?

    更新:这里的方法很好:


    这里的答案是删除
    paint()中的所有blitting
    thread,即仅从程序线程中的backbuffer刷新。这与Jochen Bedersdorfer建议的答案相反,但他的答案永远不会对我们起作用,因为程序有自己的脚本模型,该脚本模型与驱动渲染的布局模型集成在一起,因此必须按顺序进行很好

    (推测)一些问题源于Java对加速图形芯片组的多显示器支持不够出色,正如我在适应使用BufferStrategy时遇到的那样,这是direct3d+Java的一个差异

    本质上,
    paint()
    update()
    被简化为阻塞调用。这工作得更好,但有一个缺点-没有平滑的大小调整

    private class InnerFrame extends Frame() {
        public void update(Graphics g) { }
        public void paint(Graphics g) { }
        ....
    }
    
    我最终使用了缓冲区策略,尽管我对这种方法不是100%满意,因为在我看来,渲染到图像,然后将完整图像复制到缓冲区策略,然后对屏幕执行
    show()
    是低效的

    我还实现了一个基于Swing的替代方案,但我也不是特别喜欢它。它使用带有ImageIcon的JLabel,程序线程(而不是EDT)将绘制到由ImageIcon包装的图像


    我确信,当我有更多的时间更有目的地研究这个问题时,我还有一个后续问题要问,但现在我有两个工作实现,它们或多或少地解决了这里发布的最初的问题-我从发现它们中学到了很多。

    我想你的意思是“发生了什么”-问题仍然在发生。我只是试着复制将blit代码添加到paint方法中,它的行为是相同的。(FWIW它没有真正的区别,因为paint已经通过refreshFromBackBuffer间接调用了blit,但为了彻底起见,我尝试了您建议的方法。)让我困惑的是,你说drawImage是异步的。如果代码没有在AWT事件处理程序线程(即绘画线程)中调用,它将是异步的。如果绘画间接调用Blit,它应该传递图形对象,我假设drawImage是异步的(在某些情况下肯定是异步的,但不是全部?不确定).我将修改油漆()传递图形对象并查看其影响。blit发生在两个线程中-主应用程序线程和绘制线程。另一个假设是我正确诊断了问题。我可能是错的,我在stackoverflow上的帖子不是为了得到答案,而是为了深入了解如何正确管理blit从两个独立的线程开始。使用paint提供的图形没有任何区别。(理论上它与frame.getGraphics()相同。)接下来是ImageObserver的试用。不确定为什么会在两个线程中出现blit。它应该只在AWT事件线程中进行。您可以在“动画”线程中填充后缓冲区,并将blit保留在AWT事件线程的真实图形表面上。您可能要同步