当前位置: 首页 > 知识库问答 >
问题:

这种非标准Java同步模式是否有效?

尹俊雅
2023-03-14

假设我有两个线程像这样运行:

  • 在更新共享图像的像素时执行计算的线程
  • 线程B定期读取图像并将其复制到屏幕上

线程A执行工作速度很快,比如每秒100万更新,所以我怀疑经常锁定/互斥锁/监视器是个坏主意。但是如果没有锁,也没有办法从线程A到线程B建立一个发生前关系,那么通过Java内存模型(JMM规范)线程B根本不能保证看到线程A对映像的任何更新...

所以我认为,最起码的解决方案是线程A和线程B都在同一个共享锁上定期同步,但在同步块内实际上不执行任何工作——这就是导致模式不标准和可疑的原因。要用半实半伪代码进行说明:

class ComputationCanvas extends java.awt.Canvas {

    private Object lock = new Object();
    private int[] pixels = new int[1000000];

    public ComputationCanvas() {
        new Thread(this::runThreadA).start();
        new Thread(this::runThreadB).start();
    }

    private void runThreadA() {
        while (true) {
            for (1000 steps) {
                update pixels directly
                without synchornization
            }
            synchronized(lock) {}    // Blank
        }
    }

    private void runThreadB() {
        while (true) {
            Thread.sleep(100);
            synchronized(lock) {}    // Blank
            this.repaint();
        }
    }

    @Override
    public void paint(Graphics g) {
        g.drawImage(pixels, 0, 0);
    }
}

以这种方式添加空同步块是否正确地实现了将html" target="_blank">数据从线程A传输到线程B的效果?还是有其他我无法想象的解决方案?

共有1个答案

柳仲卿
2023-03-14

是的,很有效。但效果很糟糕。

只有当作者的释放发生在读者获得之前时,才会起作用。您的实现假设您正在编写的内容将在后续从ThreadB读取/更新之前完成。让您的数据一直被synchronized刷新会导致性能问题,尽管我不能确定其程度。当然,您已经使同步更细粒度了,您测试过了吗?

更好的解决方案可能是使用singleton/transfer SPSC(单生产者/单消费者)队列来存储写入线程的当前快照,并在更新时使用该快照。

int[] data = ...
Queue<int[]> queue = new ...

// Thread A
while (true) {
    for (1000 iterations or so) {
        ...
    }
    queue.add(data);
}

// Thread B
while (true) {
    int[] snapshot = queue.take(); 
    this.repaint();
}

这样做的好处是,你不需要忙碌等待,你可以等待队列阻塞或直到下一次写入。您可以跳过没有时间更新的写入。您不需要依赖于任意线程调度程序来为您规划数据刷新。

请记住,线程安全的数据结构非常适合在线程之间传递数据。

编辑:哎呀,忘了说,根据更新的进展情况,您可能希望使用数组副本来防止数据因未缓存的随机写入而被篡改。

 类似资料:
  • 问题内容: 我有一个奇怪的问题,如果可以解决,那就太好了。出于调试目的(以及其他一些目的),我在标准输出上编写了控制台Java应用程序的日志。在标准输出上写一些内容,在标准错误上打印一些错误,例如错误。问题是这两个没有完全同步,因此打印线的顺序并不总是正确的。我猜这是因为打印了很多东西,并且碰巧一个输出的缓冲区已满,所以其他输出在第一个输出刷新其缓冲区之前就已打印出来。 例如,我想这样写: 有时打

  • 问题内容: 我有两个线程,我想确保我在LinkedBlockingQueue上正确进行了同步。这正确吗?还是不需要在(messageToCommsQueue)上进行显式同步? 宣言: 方法一: 方法二: 问题答案: 是的,没有必要。JavaDoc说: BlockingQueue实现是线程安全的。

  • 问题内容: 有谁能举例说明同步方法优于同步块的优势吗? 问题答案: 在块上使用同步方法没有明显的优势。 也许唯一的一个(但我不会称其为优势)是你不需要包括对象引用。 方法: 块: 看到?完全没有优势。 但是,块确实比方法具有优势,主要是在灵活性方面,因为你可以将另一个对象用作锁,而同步该方法将锁定整个对象。 比较: 与 同样,如果方法变大,你仍然可以将同步部分分开:

  • 我正在JBOSS中实现SP启动的web浏览器SAML SSO配置文件。 我的应用程序是SP。 登录后,我希望IDP向我发送以下格式的加密断言: 对于一些国内流离失所者来说效果很好,但现在我有了一个国内流离失所者,它向我发送: 并且由于签名丢失,身份验证失败。 我的问题是:是否有SAML 2.0加密断言的标准格式,我可以告诉IDP管理员使用它?还是我必须支持这两种方式? 谢谢

  • 这个问题在我的项目中经常出现。作为一个例子,假设我有两个接口,一个从API检索信息,另一个解析这些信息。 现在,我可能需要有不同的API,因此我将有许多的实现,但每个实现都需要自己的。 这看起来与Bridge设计模式所建议的非常相似,但是该模式允许任何APIClient使用任何APIParser(我说的对吗?) 那么,有没有更好的解决方案呢?或者也许这很好,不需要重构它。 另外,也许parse不是

  • 问题内容: 这是我正在考虑使用的模式: 通常,相对于dict get / set访问,我更喜欢对象属性访问的语义,但是在某些情况下,需要像dict一样的访问(例如),并且在这些情况下,我不希望使用特殊的gettersetter方法,因此,具有共享属性的dict和object同时具有双重行为。 是否有上述模式的陷阱? 问题答案: 这是达到相同效果的一种不太“ hacky”的方法: 我 认为 您的方法