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

时间:2014-05-21 12:14:13

标签: java jpa eclipselink

对于我工作的项目中的JPA-Entities,List或Map类型的属性总是初始化为同步实现Vector和Hashtable。
(Unsynchronized 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;
    }
}

5 个答案:

答案 0 :(得分:5)

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

这并不意味着你应该进行大清理并用ArrayLists替换现有代码中的所有向量。

  • 您的代码使用列表,编程时不会发现任何差异。
  • 唯一可以预期的优势是提高绩效。
  • 很难判断您的代码是否都不依赖于向量提供的同步。

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

另外,请注意,当多个线程同时访问您的集合时,性能会受到使用向量的影响最大。因此,如果 因性能损失而决定替换Vector,那么您需要非常小心地保持访问充分同步。


编辑:你特别询问EclipseLink JPA。

如果他们要求你使用Vectors和Hashtables,那将是相当令人惊讶的,因为这意味着他们会要求你依赖过时的数据结构。 在their examples中,他们使用ArrayLists和HashMaps,因此我们可以得出结论,确实不是这样。

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

另见the documentation for the Container policy

EclipseLink和你的列表遇到的最具侵入性的时刻是你懒得加载集合。 EclipseLink将使用自己的IndirectList替换集合,该集合在内部使用Vector。请参阅What collections does jpa return?因此,在这些情况下,EclipseLink将始终为您提供Vector(!),它甚至不会影响您在集合初始化中指定的集合。

  

所以EclipseLink确实偏爱使用Vectors和使用   使用EclipseLink的向量意味着减少对象引用的复制   一个集合到另一个。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

通过eclipselink的代码库,看起来vector的用法是从较旧的代码库继承的,并且很像Vector类本身 - 遗留。 不知何故,意图是使用Vector允许多个线程在懒惰加载的关系上安全地行动 - &#34;间接&#34;用eclipselink的说法。 (更多关于概念here - 讨论的不同类型的间接是ValueHolder间接,透明间接,代理间接等。) 但是,在通常的用例中,通常不会在多个线程之间共享实体及其关系。每个线程都获得它自己的实体副本及其副本 如果在他们自己的工作单元中访问这些关系。

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

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;
    }

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

由于使用了herehere提及的Vector,因此报告的问题很少。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

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