【问题标题】:Java Synchronisation - Do I need it for this simple method?Java 同步 - 这个简单的方法需要它吗?
【发布时间】:2009-06-14 15:36:24
【问题描述】:

我有一个从多个线程调用的简单方法;

@Override
public Bitmap getFullBitmap(Filter f, ProgressCallback<String> pc) {
    // Requires synchronisation?
    Bitmap bitmap = fullMap.get(f.id);
    if(bitmap == null){
        f.setProgressCallback(pc);
        bitmap = f.e.evaluate(currentBitmap);
        fullMap.put(f.id, bitmap);
    }
    return bitmap;
}

由于所使用的对象都不是类的字段(除了 fullMap),是否可以仅调用此方法,或者可能一个线程更改 bitmap 的值,例如,当方法正在执行时?

fullMap 是一个 SoftHashMap,它维护索引的 Bitmap 对象的 SoftReferences,但过滤器的 id 用于创建它。如果这有任何意义。

我没有遇到任何问题,但我认为我可能需要它。

如果不清楚,请要求澄清,这个问题在我的脑海中是有道理的;)

编辑

  • currentBitmap 是 Bitmap 类型的对象,系统中有一个位图被认为是当前的,并由此类管理。
  • 这段代码构成了一个非常基本的缓存, 返回的位图将始终是 每个 id 都相同,而不是 在此方法之外修改。
  • 通过在 Dr Heinx 描述的 SoftHashMap 和一个 FIFO 队列 最近的 10 篇参考文献 补充说我希望避免昂贵的电话 f.e.评估。话虽如此, 调用 f.e.evaluate 将返回一个 相同的位图对象,如果它是 给定相同的输入。经过一番 认为似乎同步 该方法是个好主意,因为什么都没有 积极来自两个线程 执行此代码相同 过滤器。
  • 此外,我将位图设为最终版本 不应在创建后发生变异。

非常感谢! 加夫

【问题讨论】:

    标签: java synchronization methods


    【解决方案1】:

    2 个线程可以同时访问映射fullMap。两者都可以确定映射不包含相同键的值,各自创建一个,然后将其写回,从而插入一个键两次。

    除了效率之外,这可能不是问题。但是,它可能会造成混乱,并且可能会在未来随着您的解决方案的发展而出现问题(将来创建这些对象的成本会有多高?如果有人将代码复制/粘贴到不太合适的地方会发生什么!)

    我强烈建议在上面进行同步(很可能在 fullMap 本身而不是包含对象上进行同步,但在确定确切需要什么之前,更多上下文会很有用)

    【讨论】:

    • 指令重新排序不是您所说的竞争条件的来源。这是一个简单的多线程竞争条件。
    • 我不认为双重检查锁定问题适用于此,因为位图不是一个字段。当对象进入地图时,它应该被正确构建。此外,如果由于某种原因您只想要第一个键值对的值,那么两次向映射添加键应该是一个问题。否则,第二个值将覆盖第一个值。不过,我认为您在地图上同步可能是正确的,因为 put 本身可能不是线程安全的。
    • @Matthew - 编辑以反映插入两次密钥可能不是效率问题
    • 我已进一步编辑以删除对 DCL 的引用。你是对的,它只适用于字段
    • 两个线程创建一个值并将它们的值插入到映射中几乎总是一个问题,因为这意味着两个不同的线程将有两个他们认为相同的不同值。只是有时这不是问题,即使这样也很少是一个好主意。
    【解决方案2】:

    SoftHashMap.put 本身可能不是线程安全的。 SoftHashMap 不在标准库中,但WeakHashMap 在,并且它不同步。除了同步地图上的方法外,您可能还需要使用Collections.synchronizedMap 以确保其他方法不会同时修改地图。

    【讨论】:

      【解决方案3】:

      您绝对需要同步,因为您可以让两个线程确定f.id 不在映射中,构造然后添加一个。每个线程都将返回一个 f.id 的差异实例,即使映射只包含最后完成的那个。

      问题不是变量bitmap。这是线程安全的,因为它是单个线程的本地。但是,由于您正在执行“put-if-absent”,因此需要同步对 `fullMap 的访问(我假设它是该类的一个字段)。

      假设构建位图的成本很高,那么最好的方法就是同步方法getFullBitmap()。如果构造起来非常便宜——比同步便宜——那么我建议总是构造新对象并在ConcurrentMap 上执行putIfAbsent。但是当对象的构建成本很高时,这是一个坏主意。

      【讨论】:

        【解决方案4】:

        如果你的方法只使用传入的参数和局部变量,没有共享状态,那么我会说它是线程安全的,不需要同步。

        线程安全必须担心可变的共享状态。 fullmap 是该对象状态的一部分吗?如果是,那么您必须同步其访问权限。

        【讨论】:

          【解决方案5】:

          如果返回的位图在方法之外被修改,我会选择同步路径。当 f.id 的位图为空时,您会冒 2 个线程访问上述方法的风险,创建一个线程并将其添加到地图中,第二个线程会覆盖地图中的第一个线程。现在你有两个,一个将由线程 1 修改,但一旦线程 1 处理完它就会超出范围,另一个来自线程 2,它将保留在映射中并提供给所有未来请求者。

          【讨论】:

            【解决方案6】:

            我不确定这一点,但是如果您有代码迭代“fullMap”,AFAIK 可能会抛出一种“并发修改异常”。这不能出现在您的代码中,但可能发生在 SoftMap 的库例程中。 这可能会导致您的代码在运行时时不时地在没有明显原因的情况下中断,并且没有一个好的方法来处理这种情况。

            只是一种复杂的说法:“如果有疑问,请小心”。 顺便说一句:不要首先考虑当今计算机的性能。

            快乐的黑客

            惠伯特·吉尔

            【讨论】:

              猜你喜欢
              • 2016-09-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-25
              • 2014-05-16
              • 1970-01-01
              • 2017-07-23
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多