有啥理由用同步集合初始化实体属性?

Posted

技术标签:

【中文标题】有啥理由用同步集合初始化实体属性?【英文标题】:Any reason to initialize Entity properties with synchronized Collections?有什么理由用同步集合初始化实体属性? 【发布时间】:2014-07-10 01:49:49 【问题描述】:

对于我从事的项目中的 JPA 实体,List 或 Map 类型的属性始终初始化为同步实现 Vector 和 Hashtable。 (不同步的 ArrayList 和 HashMap 是 Java 中的标准实现,除非确实需要同步。)

有谁知道为什么需要同步集合的原因?我们使用 EclipseLink。

当我问起这件事时,没有人知道为什么会这样。似乎总是这样。也许旧版本的 EclipseLink 需要这个?

我问的原因有两个:

我更喜欢像其他任何地方一样使用标准实现 ArrayList 和 HashMap。如果安全的话。 JDK 中没有匹配的同步 Set 实现。至少不像 EclipseLink 期望的那样是可序列化的。

示例实体:

@Entity
public class Person 
    ...

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable( ... )
    private List<Role> accessRoles;


    @ElementCollection
    @CollectionTable( ... )
    @MapKeyColumn(name="KEY")
    @Column(name="VALUE")
    private Map<String, String> attrs;

    public Person() 
        // Why Vector/Hashtable instead of ArrayList/HashMap?
        accessRoles = new Vector<Role>();
        attrs = new Hashtable<String, String>();
    

    public List<Role> getAccessRoles() 
        return accessRoles;
    

    public void setAccessRoles(List<Role> accessRoles) 
        this.accessRoles = accessRoles;
    

    public Map<String, String> getAttrs() 
        return attrs;
    

    public void setAttrs(Map<String, String> attrs) 
        this.attrs = attrs;
    

【问题讨论】:

根据我的经验,由于对Java Collections Framework 的普遍无知,许多开发人员使用 Vector 和 Hashtable。在过去 15 年左右的时间里,我从未见过适当的使用方法。 @SteveC 的评论 +50。 【参考方案1】:

@flup 回答了一些有趣的参考资料,我只能做一些额外的假设:

    开发和/或规范的团队根本不知道 Collection API。 团队希望在高度并发的环境中使用代码(无论是在您的 Web 应用程序中,例如将一些实体传递给其他线程或在另一个桌面应用程序中,因为 JPA 不仅限于 WEB 应用程序)。另请注意,IndirectSet 不是线程安全的,这意味着如果团队想要编写一些线程安全的代码,他们应该采取一些额外的措施(如果他们使用 Set)!

【讨论】:

至于第一,我认为toplink是在集合类还不存在的时候写的。 Java 1.2 左右。我怀疑他们无法在每个人的代码中为他们进行大清理,因此被当前的实现选择所困扰。但是从可靠的来源了解原因会很有趣。【参考方案2】:

通过eclipselink的代码库,看起来vector的用法是从旧代码库继承而来的,很像Vector类本身——遗留。 不知何故,其目的是使用 Vector 来允许多个线程安全地处理延迟加载的关系 - eclipselink 用语中的“间接”。 (更多关于 here 的概念 - 讨论的不同类型的间接是 ValueHolder 间接、透明间接、代理间接等) 然而,在通常的用例中,实体及其关系通常不会在多个线程之间共享。每个线程都有自己的实体副本及其 如果在他们自己的工作单元中访问关系。

在 ValueHoder 间接的情况下 - ValueHoderInterface 的实现之一是 ValueHoder,它通常用向量初始化。代码的相关部分如下所示 代码注释原样。 cmets也很有趣

IndirectList.java
..........................
.........................

/**
     * INTERNAL:
     * Return the valueHolder.
     * This method used to be synchronized, which caused deadlock.
     */
    public ValueHolderInterface getValueHolder() 
        // PERF: lazy initialize value holder and vector as are normally set after creation.
        if (valueHolder == null) 
            synchronized(this) 
                if (valueHolder == null) 
                        valueHolder = new ValueHolder(new Vector(this.initialCapacity, this.capacityIncrement));
                
            
        
        return valueHolder;
    

...................
..................  

此外,由于使用了 Vector (如 here 和 here 所述),报告的问题也很少。

【讨论】:

【参考方案3】:

EclipseLink 的大部分内部结构可以追溯到 Vector 和 Hashtable 是 Java 中的标准集合类型的时代。当时的 EclipseLink 是 TopLink,它起源于 Smalltalk 的持久性框架——因此,可以说,EclipseLinks 的大部分代码实际上比 Java 本身更古老。 多年来,我一直在使用 TopLink,并且他们的集合属性的标准映射总是使用 Vector 和 Hashtable。 对我来说,仍然出现在 EclipseLink 中的 Vector 和 Hashtable 唯一合理的解释是它已经这样工作了很长时间,而且 - 因为它正在工作 - 迄今为止没有人设法改变它。

对于我自己,我再也不会使用 Vector 或 Hashtable。如果我需要同步集合,我宁愿使用 SynchronizedList ...Map 等 API。 只是我的 2 克拉。

【讨论】:

【参考方案4】:

通常不需要 Vector,而更常用的是 ArrayList。因此,如果您当前的代码库中充满了向量,这有点代码味道,明智的做法是确保您的团队成员知道其中的区别。另见What are the differences between ArrayList and Vector? 和Why is Java Vector class considered obsolete or deprecated?

这并不意味着您应该进行大清理并将现有代码中的所有向量替换为 ArrayList。

您的代码使用列表,在编程时您不会注意到任何区别。 唯一可以期待的优势是提高了性能。 很难判断您的代码是否不依赖于向量提供的同步。

因此,除非您当前遇到性能问题,或者明确(重新)设计整个代码库的同步,否则您可能会引入难以修复的并发错误而没有任何好处。

另外,请注意,当多个线程同时访问您的集合时,使用向量会显着降低性能。因此,如果您遇到性能损失并因此决定更换 Vectors,则需要非常小心以保持访问充分同步。


编辑:您专门询问 EclipseLink JPA。

如果他们要求您使用向量和哈希表,那将是相当令人惊讶的,因为这意味着他们要求您依赖过时的数据结构。 在their examples 中,他们使用了 ArrayLists 和 HashMaps,因此我们可以得出结论,事实并非如此。

更具体地研究源代码,我们可以看到他们的CollectionContainerPolicy 使用 Collection 接口并且不关心您的集合的实现。但是,当您的内部集合类是 Vector 时,它确实 令人惊讶地有特殊情况。参见例如buildContainerFromVector。而它的default container class 是Vector,虽然你可以改变它。

另见the documentation for the Container policy。

EclipseLink 和您的列表相遇的最具侵入性的时刻是当您延迟加载集合时。 EclipseLink 将用它自己的 IndirectList 替换集合,该 IndirectList 在内部使用 Vector。见What collections does jpa return? 所以在这些情况下,EclipseLink 无论如何都会给你一个 Vector(!),你在集合的初始化中指定什么集合都没有关系。

所以 EclipseLink 确实偏爱使用 Vectors 和使用 带有 EclipseLink 的向量意味着从 一个集合到另一个集合。

【讨论】:

我的问题是关于 JPA/EclipseLink,而不是一般情况。 更新了 EclipseLink 特定信息。看起来 TopLink 时代的一些剩余向量仍在 EclipseLink 实现中。 我会说样本案例是使用普通收藏品这一事实说明了一切。【参考方案5】:

JPA 不需要同步集合,它应该只与业务逻辑有关。我认为不需要这个。因为你会知道。

所以基本上建议使用不同步,它会提高性能。

【讨论】:

怎么知道需要它? 如果您的企业正在对该项目执行多线程操作,那么它可能是故意的。但这似乎不太可能在内部和实体中使用多线程。因为这是错误的。在那种情况下,我猜它会在包装器或其他东西中。 真正知道的唯一方法是搜索代码,或者删除并等待错误。如果这个模块很重要,恕我直言,请保持原样。因为如果你可以通过更改造成真正的损害这会以某种方式影响重要的模块,这将是一团糟

以上是关于有啥理由用同步集合初始化实体属性?的主要内容,如果未能解决你的问题,请参考以下文章

JavaWeb系列——Hibernate——延迟加载(转)

有啥理由不在 MCU 上增加 .bss 或 .data 部分的大小?

有啥理由不使用“受保护”的属性吗?

闭包存储属性初始化有啥好处?

有啥理由使用自动实现的属性而不是手动实现的属性?

如何处理由 setattr() 创建的属性?