保护自己免受并列(遗留)模型中的并发性

时间:2015-07-28 05:20:35

标签: java multithreading generics collections concurrency

我有一张图表。此图包含具有以下内容的节点类:

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<String> predecessors = new ArrayList<>()
    • 替换登机友好的POJO List<String> predecessors = new CopyOnWriteArrayList<>()代码
    • 需要额外的序列化程序配置和/或产生令人讨厌的XML / JSON
  • 通过将图形变异操作推送到AbstractNode类来将读取与写入模型分开,并替换getSuccesors / Precessors以返回防御性副本和/或不可变集合(例如PCollections中的那些)
    • 需要大量代码来解决概念上非常小的问题
    • 要求更新消耗此图表的巨大数量的地方,期望使用标准可变集合来代替使用AbstractNode上的方法

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

要把它变成一个咆哮一秒钟,并且有很多后见之明的好处,它很好地认识到当面对创建抽象类和它们的多个实现的选择时,或者一个代表的类依赖性(读取:由其依赖性组成),后者是普遍优选的。

没有图书馆允许我做像

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

表示LinkedHashSet<>

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

表示CopyOnWriteArrayList<>

使用这样的系统,(可能)对于我来说,从并发反向实现(例如ArrayList)切换到兼容并发的实现(例如CopyOnWrite列表或共享/独占 - )是相当简单的。锁定列表)。

1 个答案:

答案 0 :(得分: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);
    }

    ...
};