【问题标题】:Preserving thread safety using lambda使用 lambda 保持线程安全
【发布时间】:2014-11-04 20:24:54
【问题描述】:

我正在尝试更新一些代码以使用 lambda 表达式,但我在保持线程安全方面遇到了一些麻烦。

我有多个线程在运行,最终调用以下回调,该回调有一个 synchronized 方法,可将一些结果添加到 LinkedList

final List<Document> mappedDocs = new LinkedList<>();
final MapCallback<Integer, Document> mapCallback = new MapCallback<Integer, Document>() {
    @Override
    public synchronized void done(int file, List<Document> results) {
        mappedDocs.addAll(results);
    }
};

但是,当我将其转换为 lambda 表达式时,我丢失了 synchronized 关键字,并且我不完全确定如何将其取回。现在,每当我运行我的代码时,我都会收到 NullPointerException。

final MapCallback<Integer, Document> mapCallback = (int file, List<Document> results) -> mappedDocs.addAll(results);

我怎样才能使这个线程再次安全?

【问题讨论】:

  • 你不能不使用 lambda 吗?
  • @jtahlborn 我可以。只是我的 IDE 告诉我可以用 lambda 替换它,所以我想我会尝试。
  • 我将使用no(请参阅此处没有提及“同步”)。
  • 我认为这是 IDE 中的一个错误,它允许您执行此重构而不会警告您它正在​​丢弃同步。
  • 如果它必须被同步,那么你正在调用对象的某些方法或类的静态方法。因此,您可以简单地对该对象或类使用锁定,例如 the accepted answer 中建议的 assylias。

标签: java multithreading lambda thread-safety


【解决方案1】:

您可以在不同的显示器上同步它,例如:

final MapCallback<Integer, Document> mapCallback = (int file, List<Document> results) -> {
  synchronized(mappedDocs) {
    mappedDocs.addAll(results);
  }
};

或者使用线程安全结构,例如 CopyOnWriteArrayList 或 BlockingQueue。

【讨论】:

    【解决方案2】:

    我强烈建议将mappedDocs 设为线程安全的数据结构(例如来自java.util.concurrent 的数据结构),或者可能是使用Collections.synchronizedList 创建的同步包装器。

    我认为你很幸运,使用匿名内部类进行同步。之所以可行,是因为它只有一个实例,并且没有其他代码会改变 mappedDocs

    (实际上你可能会遇到内存可见性问题,即使是这样。如果其他线程调用MapCallback 来添加元素,则需要在mappedDocs 构造之后和读取添加的元素之前同步其他东西。)

    问题的根源在于,以这种方式使用的匿名内部类有点像函数,但由于新对象的创建是显而易见的,因此很容易在其上进行同步等操作。但这是相当脆弱的。如果这被重构以创建 AIC 的多个实例(例如,处理来自多个来源的文档),或者如果创建了不同的 AIC(例如,如果需要重新处理,则从列表中删除文档),则在单个 AIC 实例上同步会彻底坏掉的。

    mappedDocs 转换为线程安全的数据结构或包装器可以解决内存可见性问题和并发访问问题。这使您可以使用简单的 lambda 形式,并且可以在 mappedDocs 上引入新操作,而无需考虑对其进行操作的线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-07
      • 2014-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多