【问题标题】:Protecting myself from concurrency in an incumbered (legacy) model在现有(遗留)模型中保护自己免受并发影响
【发布时间】:2015-07-28 05:20:34
【问题描述】:

我有一个图表。该图由具有以下内容的节点类组成:

public abstract class AbstractNode{

    public Collection<AbstractNode> predecessors;
    public Collection<AbstractNode> successors;

    //accept methods for visitors
}

我最近添加了一个组件,允许您从 YAML 文件导入配置。此组件读取 YAML 文件并使用此节点类生成图形。这段代码相当慢,并且在图上进行了大量操作,包括遍历一些前辈,分析各种节点,以及决定如何更新其他节点的后继和/或前辈。

我们现有的代码有一项服务,该服务每 100 毫秒轮询一次相同的图表,以使用访问者在我们的程序中生成错误列表。访问者实际上是一类对象,它将遍历每个继任者和前任者列表一次。

这会导致并发修改异常。 :(

我的选择似乎是:

  • 将轮询服务更新为事件驱动。
    • 我们从事件驱动策略转换为轮询策略,因为事件的复杂性变得非常高。这涉及到很多微妙的时间问题。
    • 最适合模型的“纯度”。
  • 更新问题轮询服务和我的新 yaml-importer 以在线程工作队列上同步(显而易见的选择是 UI 线程,因为它们已经有了基于队列的实现)
    • 对 UI 框架的更多依赖,如果 UI 框架不可用(并且我们的应用程序确实在无头命令行模式下运行),那怎么办?
  • 实现使用可关闭可迭代对象的集合,该可迭代对象锁定其源,直到发出 close() 调用或 hasNext() 返回 false。这将允许我们在读者完成之前阻止作者。
    • 在可迭代对象上阻塞。可能相当安全,因为没有人真正使用迭代器了,但它仍然是合同的一种奇怪用法。
  • 更新 AbstractNode 以使用 CopyOnWriteArrayCollection 作为其前任和后继的支持集/列表
    • List&lt;String&gt; predecessors = new CopyOnWriteArrayList&lt;&gt;() 替换入职友好POJO List&lt;String&gt; predecessors = new ArrayList&lt;&gt;() 代码
    • 需要额外的序列化程序配置和/或产生讨厌的 XML/JSON
  • 通过将图形变异操作推送到 AbstractNode 类来分离读取和写入模型,并替换 getSuccesors/Precessors 以返回防御性副本和/或不可变集合(例如 PCollections 中的那些)
    • 需要大量代码来解决概念上非常小的问题
    • 需要更新 大量 使用此图的地方,这些地方需要标准可变集合来代替使用 AbstractNode 上的方法

真正想要的是一个围绕组合而不是围绕继承构建的集合框架。

为了把它变成一秒钟的咆哮,并且得益于(很多)事后诸葛亮,它非常清楚地认识到,当面临创建抽象类和它们的多个实现的选择时,或者一个委托给依赖项(阅读:由它的依赖项组成),后者是普遍可取的。

没有图书馆可以让我做类似的事情

private final Collection<AbstractNode> predecessors = Collections.create(
    DuplicationPolicies.ForbidDuplicates,
    Orders.InsertionOrder,
    Concurrency.ThrowOnConcurrentModification
);

对于LinkedHashSet&lt;&gt;

private final Collection<AbstractNode> predecessors = Collections.create(
    DuplicationPolicies.AllowDuplicates,
    Orders.InsertionOrder,
    Concurrency.CopyOnWrite
);

对于CopyOnWriteArrayList&lt;&gt;

有了这样一个系统,对我来说,从反对并发的实现(例如 ArrayList)切换到容忍并发的实现(例如 CopyOnWrite 列表或 Shared/Exclusive-锁定列表)。

【问题讨论】:

  • “我对 java 试图构建解决这个问题的方法感到非常失望。” ...我确定我不知道你在问我们什么. Java 是一个很好的并发开发平台,任何成功开发的高度并发项目都可以证明这一点。你需要做的是为你打算用来解决你的问题的类开发合理的多线程策略……你还没有告诉我们这些是什么。 Collections Framework 类本身不是线程安全的,这一点相对不重要。
  • 补充@scottb所说的,没有算法可以用其他语言表达,但不能用Java表达。您可能需要编写比用其他语言编写更多的 Java 行,但如果它可以用任何语言实现,那么它也可以用 Java 实现。最后全部编译成相同的机器码。
  • 我在问两件事:我提出的策略真的是全部吗?我缺少一些基础课程吗?其次:是否有一些库(可能是 jakarta 的更新版本或更完整的 google-guava-collections)可以让我编写集合行为?
  • -1 用于咆哮。但听起来你要求的是Java中persistent data structures的实现?在这种情况下,请参阅此问题:what's a good persistent collections framework for use in java?
  • 关于 Java 正在完善 @jameslarge,我知道,但汇编语言也是如此。与其说是可行性问题,不如说是表达和理解的问题。无论我实施什么解决方案,我都希望是显而易见的,而不是有人会想‘为什么我们在这里使用 XYZ,而我们在其他任何地方都使用 ABC?’

标签: java multithreading generics collections concurrency


【解决方案1】:

在 java 中还没有制作复合集合,但是您可以使用标准的 java 集合和一些附加对象作为类的成员轻松地手动实现它们。

由于您总是使用Orders.InsertionOrder 策略,因此类似列表的基本集合最适合您。您可以使用CopyOnWriteArrayList 作为基本集合来实现Concurrency.CopyOnWrite 策略,使用简单的ArrayList 来实现Concurrency.ThrowOnConcurrentModification

对于DuplicationPolicies 使用单个bool canAddElem(collection, elem) 方法的附加对象就足够了。

类似的东西:

public class CompositeCollection extends Collection<AbstractNode>
{
    private Collection<AbstractNode> baseCollection;
    private filter;

    public CompositCollection(DuplicationPolicies dPolicy, Concurrency cPolicy)
    {
        if(dPolicy == DuplicationPolicies.AllowDuplicates)
        {
            filter = new AllowDuplicatesFilter();
        }
        else
        {
            filter = new ForbidDuplicatesFilter();
        }
        if(cPolicy == Concurrency.Concurrency.CopyOnWrite)
        {
            baseCollection = new CopyOnWriteArrayList<AbstractNode>();
        }
        else
        {
            baseCollection = new ArrayList<AbstractNode>();
        }
    }

    public bool add(AbstractNode node)
    {
        if(!filter->canAddElem(baseCollection, node)) {return false; }

        return baseCollection->add(node);
    }

    ...
};

【讨论】:

  • 我也有这个想法(什么自尊的程序员没有'好吧,我自己做!'),但我认为你低估了需要的工作量要完成。首先,我非常怀疑需要做一些非常严肃的大型设计工作来确定接口和枚举应该是什么。理想情况下,我会有一个 Collection 类,其中包含每个事件的处理程序/侦听器列表,而不必打开枚举对象。
  • 设计适合大量用户总是很困难的。实际上,我在这里没有看到复合集合的必要性:您已经实现了集合的所有属性,并且只希望集合是线程安全的。没有标准的集合,适合你的需要,所以你需要自己实现。在单个附加属性的情况下不需要复合模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 2012-04-05
  • 2015-11-19
  • 2012-12-20
相关资源
最近更新 更多